/*
  PCE.c
    Primary Composition Engine
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <setjmp.h>
#include <EIMIL.h>
#include <EIMILint.h>

#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

/********************************************************************************
			        Declarations
*********************************************************************************/

#define PCE_MAX_NUMBER ((1 << 23) - 1)
#define PCE_DICTIONARY_DEFAULT_SIZE 17

#define PCE_CALL_MAX_DEPTH 16

typedef struct PCE_code PCE_code;
typedef struct PCE_data PCE_data;
typedef struct PCE_context PCE_context;
typedef struct PCE_funcproto PCE_funcproto;
typedef struct PCE_function PCE_function;
typedef struct PCE_inst_template PCE_inst_template;
typedef struct PCE_parse_context PCE_parse_context;
typedef struct PCE_label PCE_label;
typedef struct PCE_labelset PCE_labelset;

typedef EIMIL_value* (*PCE_EXEC_HANDLER)(PCE_context *pctx);

struct PCE_data {
    PCE_code *pcode;
};

typedef enum PCE_ERROR_CODE (*PCE_SEH_TRY_FUNCTION)(PCE_context *pctx,
						    void *arg);
typedef enum PCE_ERROR_CODE (*PCE_SEH_CATCH_FUNCTION)(PCE_context *pctx,
						      int ecode,
						      void *throwarg,
						      void *catcharg);
typedef struct PCE_SEH_catchblock PCE_SEH_catchblock;
typedef struct PCE_SEH_block PCE_SEH_block;
struct PCE_SEH_catchblock {
    int type;
    PCE_SEH_CATCH_FUNCTION pcfs;
    void *catcharg;
    PCE_SEH_catchblock *pnext;
};

struct PCE_SEH_block {
    jmp_buf jmp;
    void *arg;
    PCE_SEH_catchblock *pcatchers;
    PCE_SEH_block *pnext;
};

struct PCE_context {
    int depth;
    PCE_code *pcur;
    EIMIL_data *ped;
    EIMIL_dictionary *pdic;
    EIMIL_dictionary *pdic_f;
    EIMIL_symbol *psym_cev;
    PCE_data *ppce_data;
    EIMIL_dictionary *pfuncdic[PCE_CALL_MAX_DEPTH];
    PCE_SEH_block *pseh;
};

struct PCE_funcproto {
    EIMIL_symbol *psym;
    enum EIMIL_TYPE type;
};

struct PCE_function {
    int nargs;
    PCE_funcproto *pfp;
    EIMIL_dictionary *pdic;
    int rettype;
    PCE_code *pc;
};

enum PCE_CODE_TYPE {
	PCE_CODE_INST,
	PCE_CODE_VALUE,

	PCE_CODE_MAIN,
	PCE_CODE_DEFUN,

	PCE_CODE_JMP,
	PCE_CODE_UNRESOLVED_JMP,
	PCE_CODE_COND_JMP,
	PCE_CODE_UNRESOLVED_COND_JMP,
	PCE_CODE_COND_NOT_JMP,
	PCE_CODE_UNRESOLVED_COND_NOT_JMP,

	PCE_CODE_SYMBOL = (1 << 16),
	PCE_CODE_FUNCTION
};

#define PCE_ARG_MAX 5
struct PCE_inst_template {
    enum PCE_CODE_TYPE code_type;
    PCE_EXEC_HANDLER handler;
    enum EIMIL_TYPE result;
    int newmode;
    int required_type[PCE_ARG_MAX];
};

#define PCE_SET_REQUIRED_TYPES(PCX, PIT) \
  (memcpy((PCX)->required_type, (PIT)->required_type, sizeof((PCX)->required_type)))

struct PCE_label {
    int id;
    PCE_code *pc;
    EIMIL_symbol *psym;
    /* If this flag is set, the label will be 
       set to the next code.  */
    int nextp;
};

struct PCE_labelset {
    int labelnum;
    int allocednum;
    EIMIL_dictionary *pdic;
    PCE_label *pl;
};

struct PCE_parse_context {
    PCE_context *pctx;
    int idx;
    unsigned int mode;
    int required_type[PCE_ARG_MAX];
    enum EIMIL_TYPE result;
    EIMIL_dictionary *pdic;
    EIMIL_symbol *psym;
    PCE_function *pf;
    PCE_code *pc;
    PCE_code *pc_head;
    PCE_labelset *plabels;
    int labelid;
    int labelid2;

    PCE_parse_context *pnext;
};

struct PCE_code {
    enum PCE_CODE_TYPE type;
    union {
	PCE_EXEC_HANDLER h;
	EIMIL_value *pv;
	EIMIL_SYMID symid;
	PCE_function *pf;
	PCE_code *pc_to;
	int to_labelid;
    } val;
    void *poption;
    PCE_code *parg;
    PCE_code *pnext;
};

enum PCE_CONTEXT_MODE {
	PCE_IN_MAIN = (1 << 0),
	PCE_IN_TRY = (1 << 1),
	PCE_IN_CATCH = (1 << 2),
	PCE_IN_DEFVAR = (1 << 3),
	PCE_DEFVAR_NIL = (1 << 4),
	PCE_DEFVAR_DONE = (1 << 5),
	PCE_IN_DEFUN = (1 << 6),
	PCE_IN_DEFTABLE = (1 << 7),
	PCE_IN_DEFPATTERN = (1 << 8),

	PCE_CONTEXT_MODE_PERSISTENT_MASK = (1 << 16) - 1,

	PCE_IN_IF = (1 << 16),
	PCE_IN_ELSE = (1 << 17),
	PCE_IN_E = (1 << 18),
	PCE_IN_SELECT = (1 << 19),
	PCE_SELECT_HAS_DEFAULT = (1 << 20),
	PCE_ONLY_IN_MAIN_STATEMENT = (1 << 21)
};
#define PCE_NEW_PARSER_CONTEXT_MODE(pcx, m) \
(((pcx)->mode & PCE_CONTEXT_MODE_PERSISTENT_MASK) | (m))

#define PCE_SET_PARSER_CONTEXT_MODE(pcx, m) \
((pcx)->mode | (m))

#define PCE_RESET_PARSER_CONTEXT_MODE(pcx, m) \
((pcx)->mode & (~(m)))

#define PCE_ADD_CODE(PCX, PCODE) \
(((PCODE)->pnext = (PCX)->pc), ((PCX)->pc = (PCODE)), ((PCX)->idx++))


/********************************************************************************
			    PCE error handling.
*********************************************************************************/

enum PCE_ERROR_CODE {
	PCE_ANY_ERROR = -1,
	PCE_SUCCESS = 0,
	PCE_UNKNOWN_ERROR,
	PCE_MEMORY_ERROR,
	PCE_LITERAL_OVERFLOW,
	PCE_DEC_NUMBER_ERROR,
	PCE_HEX_NUMBER_ERROR,
	PCE_CHAR_LITERAL_ERROR,
	PCE_MTEXT_LITERAL_ERROR,
	PCE_NOT_VARIABLE_ERROR,
	PCE_NOT_FUNCTION_ERROR,
	PCE_NOT_EXCEPTION_ERROR,
	PCE_LABEL_NOT_DEFINED_ERROR,

	PCE_PARSE_NOT_LITERAL,
	PCE_PARSE_SYNTAX_ERROR,
	PCE_PARSE_UNKNOWN_SYMBOL_ERROR,
	PCE_PARSE_INVALID_SYMBOL_ERROR,
	PCE_PARSE_RETURN_IN_DEFUN_ERROR,

	PCE_TOO_FEW_ARGUMENTS_ERROR,
	PCE_WRONG_TYPE_ARGUMENT_ERROR,
	PCE_VARIABLE_CONSTANT_ERROR,
	PCE_OUT_OF_RANGE_ERROR,
	PCE_OVER_EVAL_DEPTH_ERROR,

	PCE_DEFVAR_DONE_ERROR,
	PCE_DEFVAR_NIL_ERROR,

	PCE_RETURN_JMP_ERROR,
	PCE_WAIT_NEXT_EVENT_ERROR,

	PCE_NO_MORE_ARG_ERROR
};
static void
PCE_set_error(
    EIMIL_data *ped,
    enum PCE_ERROR_CODE code
)
{
    switch (code) {
      case PCE_LITERAL_OVERFLOW:
       EIMIL_set_error_pt(ped, NULL, "Overflow in the literal.");
       return;
      case PCE_DEC_NUMBER_ERROR:
       EIMIL_set_error_pt(ped, NULL, "Syntax error in the decimal number.");
       break;
      case PCE_HEX_NUMBER_ERROR:
       EIMIL_set_error_pt(ped, NULL, "Syntax error in the hexadecimal number.");
       break;
      case PCE_CHAR_LITERAL_ERROR:
       EIMIL_set_error_pt(ped, NULL, "Syntax error in the char literal.");
       break;
      case PCE_MTEXT_LITERAL_ERROR:
       EIMIL_set_error_pt(ped, NULL, "Syntax error in the mtext literal.");
       break;
      case PCE_TOO_FEW_ARGUMENTS_ERROR:
       EIMIL_set_error_pt(ped, NULL, "Too few arguments.");
       return;
      case PCE_DEFVAR_DONE_ERROR:
       EIMIL_set_error_pt(ped, NULL, "Initiali value must be only one in defvar.");
       return;
      case PCE_DEFVAR_NIL_ERROR:
       EIMIL_set_error_pt(ped, NULL, "The variable in defvar must be initialized to nil.");
       return;
      case PCE_PARSE_UNKNOWN_SYMBOL_ERROR:
       EIMIL_set_error_pt(ped, NULL, "Unknown symbol.");
       return;
      case PCE_PARSE_RETURN_IN_DEFUN_ERROR:
       EIMIL_set_error_pt(ped, NULL, "`return' must be in `defun'.");
       return;
      case PCE_NOT_VARIABLE_ERROR:
       EIMIL_set_error_pt(ped, NULL, "It is not a variable.");
       return;
      case PCE_NOT_FUNCTION_ERROR:
       EIMIL_set_error_pt(ped, NULL, "It is not a function.");
       return;
      case PCE_NOT_EXCEPTION_ERROR:
       EIMIL_set_error_pt(ped, NULL, "It is not an exception.");
       return;
      default:
       EIMIL_set_error_pt(ped, NULL, "PCE internal error (parser).");
    }

    return;
}

/********************************************************************************
			    PCE document template
*********************************************************************************/

static const UTF8* PCE_classname = "com.sun.iiim.pce1.s1";
static const UTF8* PCE_xmlns_uri = "http://www.OpenI18N.org/EIMIL/NS/PCE/1.0";

#define DECLARE_PARSER_METHOD(name) \
static int PCE_##name##_parser( \
    EIMIL_data *ped, \
    EIMIL_attrs *patr, \
    enum EIMIL_TAG_TYPE type, \
    UTF8 *pchars, \
    void **pprivate \
)
#define DEFINE_PARSER_METHOD(name) DECLARE_PARSER_METHOD(name)

static int
PCE_generic_inst_parser(
    EIMIL_data *ped,
    EIMIL_attrs *patr,
    enum EIMIL_TAG_TYPE type,
    UTF8 *pchars,
    void **pprivate
);

/*
  instrunction element template.
 */

#define PCE_noarg_option (EIMIL_element_EMPTY | EIMIL_element_multiple)
#define PCE_arg_option (EIMIL_element_multiple | EIMIL_allow_PCDATA | EIMIL_PCDATA_QUOTED_TOKEN)


#define DEFINE_SINST_TEMPLATE_1(name, kind, type, mode, arg1) \
static PCE_inst_template \
PCE_##name##_inst_template = {kind, NULL, type, mode, {arg1}}

#define DEFINE_SINST_TEMPLATE_0(name, kind, type, mode) \
  DEFINE_SINST_TEMPLATE_1(name, kind, type, mode, 0)

#define DEFINE_INST_TEMPLATE_5(name, type, mode, arg1, arg2, arg3, arg4, arg5) \
static PCE_inst_template \
PCE_##name##_inst_template = {PCE_CODE_INST, PCE_##name##_exec, type, mode, \
                              {arg1, arg2, arg3, arg4, arg5}}
#define DEFINE_INST_TEMPLATE_4(name, type, mode, arg1, arg2, arg3, arg4) \
  DEFINE_INST_TEMPLATE_5(name, type, mode, arg1, arg2, arg3, arg4, 0)
#define DEFINE_INST_TEMPLATE_3(name, type, mode, arg1, arg2, arg3) \
  DEFINE_INST_TEMPLATE_5(name, type, mode, arg1, arg2, arg3, 0, 0)
#define DEFINE_INST_TEMPLATE_2(name, type, mode, arg1, arg2) \
  DEFINE_INST_TEMPLATE_5(name, type, mode, arg1, arg2, 0, 0, 0)
#define DEFINE_INST_TEMPLATE_1(name, type, mode, arg1) \
  DEFINE_INST_TEMPLATE_5(name, type, mode, arg1, 0, 0, 0, 0)
#define DEFINE_INST_TEMPLATE_0(name, type, mode) \
  DEFINE_INST_TEMPLATE_5(name, type, mode, 0, 0, 0, 0, 0)

#define INST_ELEMENT_TEMPLATE(name, strname) \
{(strname), PCE_generic_inst_parser, NULL, \
 PCE_arg_option, PCE_statement_template, \
 &PCE_##name##_inst_template}

#define INST_ELEMENT_TEMPLATE_NOARG(name, strname) \
{(strname), PCE_generic_inst_parser, NULL, \
 PCE_noarg_option, NULL, \
 &PCE_##name##_inst_template}

#define INST_ELEMENT_TEMPLATE_OPTION(name, strname, option) \
{(strname), PCE_generic_inst_parser, NULL, \
 (option), PCE_statement_template, \
 &PCE_##name##_inst_template}

#define DECLARE_EXEC_METHOD(name) \
static EIMIL_value* PCE_##name##_exec( \
    PCE_context *pctx \
)
#define DEFINE_EXEC_METHOD(name) DECLARE_EXEC_METHOD(name)

DECLARE_PARSER_METHOD(PCE);

DECLARE_PARSER_METHOD(mnemonic);
DECLARE_PARSER_METHOD(pattern);
DECLARE_PARSER_METHOD(key);
DECLARE_PARSER_METHOD(defvar);
DECLARE_PARSER_METHOD(defun);
DEFINE_SINST_TEMPLATE_1(defun, PCE_CODE_DEFUN, EIMIL_TYPE_NONE, 0,
			EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1);
DECLARE_PARSER_METHOD(deftable);
DECLARE_PARSER_METHOD(defkeymap);
DECLARE_PARSER_METHOD(defpattern);
DECLARE_PARSER_METHOD(main);
DEFINE_SINST_TEMPLATE_1(main, PCE_CODE_MAIN, EIMIL_TYPE_NONE, 0,
			EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1);

DECLARE_PARSER_METHOD(while);
DECLARE_PARSER_METHOD(go);
DECLARE_PARSER_METHOD(label);
DECLARE_PARSER_METHOD(undo);
DECLARE_EXEC_METHOD(undo);
DEFINE_INST_TEMPLATE_0(undo, EIMIL_TYPE_NONE, 0);
DECLARE_PARSER_METHOD(mark_undo);
DECLARE_EXEC_METHOD(mark_undo);
DEFINE_INST_TEMPLATE_0(mark_undo, EIMIL_TYPE_NONE, 0);
DECLARE_PARSER_METHOD(try);
DECLARE_EXEC_METHOD(try);
DEFINE_INST_TEMPLATE_1(try, EIMIL_TYPE_NONE, 0,
		       EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1);
DECLARE_PARSER_METHOD(catch);
DECLARE_EXEC_METHOD(catch);
DEFINE_INST_TEMPLATE_1(catch, EIMIL_TYPE_NONE, 0,
		       EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1);
DECLARE_PARSER_METHOD(throw);
DECLARE_EXEC_METHOD(throw);
DEFINE_INST_TEMPLATE_0(throw, EIMIL_TYPE_NONE, 0);
DECLARE_PARSER_METHOD(if);
DECLARE_PARSER_METHOD(else);

DECLARE_PARSER_METHOD(keycase);
DECLARE_EXEC_METHOD(keycase);
DEFINE_INST_TEMPLATE_1(keycase, EIMIL_TYPE_NONE, 0,
		       EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1);
DECLARE_PARSER_METHOD(keymap);
DECLARE_EXEC_METHOD(keymap);
DEFINE_INST_TEMPLATE_1(keymap, EIMIL_TYPE_NONE, 0,
		       EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1);

DECLARE_PARSER_METHOD(case);
DECLARE_PARSER_METHOD(default);
DECLARE_PARSER_METHOD(select);

DECLARE_EXEC_METHOD(toggle_preedit);
DEFINE_INST_TEMPLATE_1(toggle_preedit, EIMIL_TYPE_BOOL, 0, EIMIL_TYPE_BOOL);
DECLARE_EXEC_METHOD(toggle_lookup_choice);
DEFINE_INST_TEMPLATE_1(toggle_lookup_choice, EIMIL_TYPE_BOOL, 0, EIMIL_TYPE_BOOL);
DECLARE_EXEC_METHOD(keyeventp);
DEFINE_INST_TEMPLATE_1(keyeventp, EIMIL_TYPE_BOOL, 0, EIMIL_TYPE_EVENT);

DECLARE_EXEC_METHOD(or);
DEFINE_INST_TEMPLATE_1(or, EIMIL_TYPE_BOOL, 0,
		       EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1);
DECLARE_EXEC_METHOD(and);
DEFINE_INST_TEMPLATE_1(and, EIMIL_TYPE_BOOL, 0,
		       EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1);
DECLARE_EXEC_METHOD(not);
DEFINE_INST_TEMPLATE_1(not, EIMIL_TYPE_BOOL, 0, EIMIL_TYPE_ANY);
DECLARE_EXEC_METHOD(gt);
DEFINE_INST_TEMPLATE_2(gt, EIMIL_TYPE_BOOL, 0,
		       EIMIL_TYPE_NUMBER, EIMIL_TYPE_NUMBER);
DECLARE_EXEC_METHOD(lt);
DEFINE_INST_TEMPLATE_2(lt, EIMIL_TYPE_BOOL, 0,
		       EIMIL_TYPE_NUMBER, EIMIL_TYPE_NUMBER);
DECLARE_EXEC_METHOD(le);
DEFINE_INST_TEMPLATE_2(le, EIMIL_TYPE_BOOL, 0,
		       EIMIL_TYPE_NUMBER, EIMIL_TYPE_NUMBER);
DECLARE_EXEC_METHOD(ge);
DEFINE_INST_TEMPLATE_2(ge, EIMIL_TYPE_BOOL, 0,
		       EIMIL_TYPE_NUMBER, EIMIL_TYPE_NUMBER);
DECLARE_EXEC_METHOD(eql);
DEFINE_INST_TEMPLATE_2(eql, EIMIL_TYPE_BOOL, 0,
		       EIMIL_TYPE_NUMBER, EIMIL_TYPE_NUMBER);

DECLARE_EXEC_METHOD(propval);
DEFINE_INST_TEMPLATE_2(propval, EIMIL_TYPE_ANY, 0,
		       EIMIL_TYPE_PROP, EIMIL_TYPE_NUMBER);

DECLARE_EXEC_METHOD(propsize);
DEFINE_INST_TEMPLATE_1(propsize, EIMIL_TYPE_NUMBER, 0, EIMIL_TYPE_PROP);
DECLARE_EXEC_METHOD(propmbeg);
DEFINE_INST_TEMPLATE_1(propmbeg, EIMIL_TYPE_NUMBER, 0, EIMIL_TYPE_PROP);
DECLARE_EXEC_METHOD(propmend);
DEFINE_INST_TEMPLATE_1(propmend, EIMIL_TYPE_NUMBER, 0, EIMIL_TYPE_PROP);
DECLARE_EXEC_METHOD(evval);
DEFINE_INST_TEMPLATE_1(evval, EIMIL_TYPE_NUMBER, 0, EIMIL_TYPE_EVENT);
DECLARE_EXEC_METHOD(evmod);
DEFINE_INST_TEMPLATE_1(evmod, EIMIL_TYPE_NUMBER, 0, EIMIL_TYPE_EVENT);
DECLARE_EXEC_METHOD(strlen);
DEFINE_INST_TEMPLATE_1(strlen, EIMIL_TYPE_NUMBER, 0, EIMIL_TYPE_MTEXT);
DECLARE_EXEC_METHOD(strcmp);
DEFINE_INST_TEMPLATE_2(strcmp, EIMIL_TYPE_NUMBER, 0,
		       EIMIL_TYPE_MTEXT, EIMIL_TYPE_MTEXT);
DECLARE_EXEC_METHOD(add);
DEFINE_INST_TEMPLATE_1(add, EIMIL_TYPE_NUMBER, 0,
		       EIMIL_TYPE_NUMBER | EIMIL_TYPE_OPTION1);
DECLARE_EXEC_METHOD(sub);
DEFINE_INST_TEMPLATE_1(sub, EIMIL_TYPE_NUMBER, 0,
		       EIMIL_TYPE_NUMBER | EIMIL_TYPE_OPTION1);
DECLARE_EXEC_METHOD(mul);
DEFINE_INST_TEMPLATE_1(mul, EIMIL_TYPE_NUMBER, 0,
		       EIMIL_TYPE_NUMBER | EIMIL_TYPE_OPTION1);
DECLARE_EXEC_METHOD(div);
DEFINE_INST_TEMPLATE_1(div, EIMIL_TYPE_NUMBER, 0,
		       EIMIL_TYPE_NUMBER | EIMIL_TYPE_OPTION1);
DECLARE_EXEC_METHOD(mod);
DEFINE_INST_TEMPLATE_1(mod, EIMIL_TYPE_NUMBER, 0,
		       EIMIL_TYPE_NUMBER | EIMIL_TYPE_OPTION1);
DECLARE_EXEC_METHOD(bor);
DEFINE_INST_TEMPLATE_1(bor, EIMIL_TYPE_NUMBER, 0,
		       EIMIL_TYPE_NUMBER | EIMIL_TYPE_OPTION1);
DECLARE_EXEC_METHOD(band);
DEFINE_INST_TEMPLATE_1(band, EIMIL_TYPE_NUMBER, 0,
		       EIMIL_TYPE_NUMBER | EIMIL_TYPE_OPTION1);
DECLARE_EXEC_METHOD(bxor);
DEFINE_INST_TEMPLATE_1(bxor, EIMIL_TYPE_NUMBER, 0,
		       EIMIL_TYPE_NUMBER | EIMIL_TYPE_OPTION1);
DECLARE_EXEC_METHOD(UCSval);
DEFINE_INST_TEMPLATE_1(UCSval, EIMIL_TYPE_NUMBER, 0, EIMIL_TYPE_CHAR);

DECLARE_EXEC_METHOD(propadd);
DEFINE_INST_TEMPLATE_2(propadd, EIMIL_TYPE_PROP, 0,
		       EIMIL_TYPE_PROP, EIMIL_TYPE_ANY);
DECLARE_EXEC_METHOD(propcopy);
DEFINE_INST_TEMPLATE_1(propcopy, EIMIL_TYPE_PROP, 0, EIMIL_TYPE_PROP);
DECLARE_EXEC_METHOD(evchar);
DEFINE_INST_TEMPLATE_1(evchar, EIMIL_TYPE_CHAR, 0, EIMIL_TYPE_EVENT);
DECLARE_EXEC_METHOD(strref);
DEFINE_INST_TEMPLATE_2(strref, EIMIL_TYPE_CHAR, 0,
		       EIMIL_TYPE_MTEXT, EIMIL_TYPE_NUMBER);
DECLARE_EXEC_METHOD(makechar);
DEFINE_INST_TEMPLATE_1(makechar, EIMIL_TYPE_CHAR, 0, EIMIL_TYPE_NUMBER);
DECLARE_EXEC_METHOD(evtype);
DEFINE_INST_TEMPLATE_1(evtype, EIMIL_TYPE_MTEXT, 0, EIMIL_TYPE_EVENT);
DECLARE_EXEC_METHOD(evmtext);
DEFINE_INST_TEMPLATE_1(evmtext, EIMIL_TYPE_MTEXT, 0, EIMIL_TYPE_EVENT);
DECLARE_EXEC_METHOD(concat);
DEFINE_INST_TEMPLATE_1(concat, EIMIL_TYPE_MTEXT, 0,
		       EIMIL_TYPE_CHAR | EIMIL_TYPE_MTEXT | EIMIL_TYPE_OPTION1);
DECLARE_EXEC_METHOD(substr);
DEFINE_INST_TEMPLATE_3(substr, EIMIL_TYPE_MTEXT, 0,
		       EIMIL_TYPE_MTEXT, EIMIL_TYPE_NUMBER,
		       EIMIL_TYPE_NUMBER | EIMIL_TYPE_OPTION2);
DECLARE_EXEC_METHOD(next);
DEFINE_INST_TEMPLATE_2(next, EIMIL_TYPE_NONE, PCE_ONLY_IN_MAIN_STATEMENT,
		       EIMIL_TYPE_EVENT, EIMIL_TYPE_EVENT | EIMIL_TYPE_OPTION2);
DECLARE_EXEC_METHOD(makeev);
DEFINE_INST_TEMPLATE_5(makeev, EIMIL_TYPE_EVENT, 0,
		       EIMIL_TYPE_MTEXT,
		       EIMIL_TYPE_NUMBER | EIMIL_TYPE_NIL,
		       EIMIL_TYPE_NUMBER | EIMIL_TYPE_NIL,
		       EIMIL_TYPE_CHAR | EIMIL_TYPE_NIL,
		       EIMIL_TYPE_MTEXT | EIMIL_TYPE_NIL);
DECLARE_EXEC_METHOD(commit);
DEFINE_INST_TEMPLATE_1(commit, EIMIL_TYPE_NONE, 0, EIMIL_TYPE_MTEXT);
DECLARE_EXEC_METHOD(unroll);
DEFINE_INST_TEMPLATE_1(unroll, EIMIL_TYPE_NONE, 0, EIMIL_TYPE_EVENT);
DECLARE_EXEC_METHOD(propdel);
DEFINE_INST_TEMPLATE_2(propdel, EIMIL_TYPE_NONE, 0,
		       EIMIL_TYPE_PROP, EIMIL_TYPE_NUMBER);
DECLARE_EXEC_METHOD(addmprop);
DEFINE_INST_TEMPLATE_4(addmprop, EIMIL_TYPE_NONE, 0,
		       EIMIL_TYPE_MTEXT, EIMIL_TYPE_PROP,
		       EIMIL_TYPE_NUMBER, EIMIL_TYPE_NUMBER);
DECLARE_EXEC_METHOD(delmprop);
DEFINE_INST_TEMPLATE_1(delmprop, EIMIL_TYPE_NONE, 0, EIMIL_TYPE_PROP);
DECLARE_EXEC_METHOD(setmprop);
DEFINE_INST_TEMPLATE_4(setmprop, EIMIL_TYPE_NONE, 0,
		       EIMIL_TYPE_MTEXT, EIMIL_TYPE_PROP,
		       EIMIL_TYPE_NUMBER, EIMIL_TYPE_NUMBER);

DECLARE_PARSER_METHOD(return);
DECLARE_EXEC_METHOD(return);
DEFINE_INST_TEMPLATE_1(return, EIMIL_TYPE_NONE, 0, EIMIL_TYPE_ANY);

DECLARE_PARSER_METHOD(tblkeymaxsize);
DECLARE_EXEC_METHOD(tblkeymaxsize);
DECLARE_PARSER_METHOD(tblvalmaxsize);
DECLARE_EXEC_METHOD(tblvalmaxsize);
DECLARE_PARSER_METHOD(tblref);
DECLARE_EXEC_METHOD(tblref);

DECLARE_PARSER_METHOD(makeprop);
DECLARE_EXEC_METHOD(makeprop);
DECLARE_PARSER_METHOD(getmprop);
DECLARE_EXEC_METHOD(getmprop);
DECLARE_PARSER_METHOD(findmprop);
DECLARE_EXEC_METHOD(findmprop);
DECLARE_PARSER_METHOD(interact);
DECLARE_EXEC_METHOD(interact);

DECLARE_PARSER_METHOD(match);
DECLARE_EXEC_METHOD(match);

DECLARE_PARSER_METHOD(forward);
DECLARE_EXEC_METHOD(forward);
DECLARE_PARSER_METHOD(send);
DECLARE_EXEC_METHOD(send);

DECLARE_PARSER_METHOD(set);
DECLARE_EXEC_METHOD(set);

DECLARE_PARSER_METHOD(e);
DECLARE_EXEC_METHOD(e);

#define EIMIL_TEMPLATE_NUM(e) (sizeof(e) / sizeof(EIMIL_element_template))

static EIMIL_element_template
PCE_statement_template[];

/* */
static EIMIL_element_template
PCE_if_else_template[] = {
	{"else", PCE_else_parser, NULL, 
	 EIMIL_element_0or1 | EIMIL_element_lock_template
	 | EIMIL_allow_PCDATA | EIMIL_PCDATA_QUOTED_TOKEN,
	 PCE_statement_template, NULL},
	{NULL, NULL, NULL, 0, NULL, NULL}
};
static EIMIL_element_template
PCE_if_full_template[];

static EIMIL_attr_template
PCE_attr_keycase[] = {{"code", EIMIL_attr_IMPLIED, NULL},
		      {"char", EIMIL_attr_IMPLIED, NULL},
		      {"mod", EIMIL_attr_IMPLIED, ""},
		      {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_keymap[] = {{"k", EIMIL_attr_REQUIRED, NULL},
		     {NULL, 0, NULL}};

/* */
static EIMIL_element_template
PCE_select_template[] = {
	{"case", PCE_case_parser, NULL, 
	 PCE_arg_option, PCE_statement_template, NULL},
	{"default", PCE_default_parser, NULL, 
	 EIMIL_element_0or1 | EIMIL_element_lock_template
	 | EIMIL_allow_PCDATA | EIMIL_PCDATA_QUOTED_TOKEN,
	 PCE_statement_template, NULL},
	{NULL, NULL, NULL, 0, NULL}
};

/* */
static EIMIL_element_template
PCE_try_catch_template[] = {
	INST_ELEMENT_TEMPLATE_OPTION(catch, "catch",
				     PCE_arg_option
				     | EIMIL_element_lock_template),
	{NULL, NULL, NULL, 0, NULL}
};

static EIMIL_element_template
PCE_try_full_template[];

static EIMIL_attr_template
PCE_attr_go[] = {{"to", EIMIL_attr_REQUIRED, NULL},
		 {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_label[] = {{"name", EIMIL_attr_REQUIRED, NULL},
		    {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_catch[] = {{"exc", EIMIL_attr_REQUIRED, NULL},
		    {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_throw[] = {{"exc", EIMIL_attr_REQUIRED, NULL},
		    {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_interact[] = {{"op", EIMIL_attr_REQUIRED, NULL},
		       {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_next[] = {{"remove", EIMIL_attr_NORMAL, "true"},
		   {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_forward[] = {{"to", EIMIL_attr_IMPLIED, NULL},
		      {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_send[] = {{"to", EIMIL_attr_IMPLIED, NULL},
		   {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_e[] = {{"f", EIMIL_attr_REQUIRED, NULL},
		{NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_set[] = {{"v", EIMIL_attr_REQUIRED, NULL},
		  {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_match[] = {{"p", EIMIL_attr_REQUIRED, NULL},
		    {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_tblref[] = {{"t", EIMIL_attr_REQUIRED, NULL},
		     {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_tblkeymaxsize[] = {{"t", EIMIL_attr_REQUIRED, NULL},
			    {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_tblvalmaxsize[] = {{"t", EIMIL_attr_REQUIRED, NULL},
			    {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_makeprop[] = {{"p", EIMIL_attr_REQUIRED, NULL},
		       {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_propval[] = {{"p", EIMIL_attr_REQUIRED, NULL},
		      {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_getmprop[] = {{"p", EIMIL_attr_REQUIRED, NULL},
		       {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_findmprop[] = {{"p", EIMIL_attr_REQUIRED, NULL},
			{NULL, 0, NULL}};

static EIMIL_element_template
PCE_statement_template[] = {
	/* Flow control */
	{"while", PCE_while_parser, NULL,
	 PCE_arg_option, PCE_statement_template, NULL},
	/* undo */
	INST_ELEMENT_TEMPLATE_NOARG(undo, "undo"),
	INST_ELEMENT_TEMPLATE_NOARG(mark_undo, "mark-undo"),
	/* STH */
	{"try", PCE_try_parser, NULL,
	 PCE_arg_option, PCE_try_full_template,
	 &PCE_try_inst_template},
	/* flow */
	{"if", PCE_if_parser, NULL,
	 PCE_arg_option, PCE_if_full_template, NULL},
	{"select", PCE_select_parser, NULL,
	 EIMIL_element_multiple, PCE_select_template, NULL},

	/* BOOL */
	INST_ELEMENT_TEMPLATE(toggle_preedit, "toggle-preedit"),
	INST_ELEMENT_TEMPLATE(toggle_lookup_choice, "toggle-lookup-choice"),
	INST_ELEMENT_TEMPLATE(keyeventp, "keyeventp"),
	INST_ELEMENT_TEMPLATE(or, "or"),
	INST_ELEMENT_TEMPLATE(and, "and"),
	INST_ELEMENT_TEMPLATE(not, "not"),
	INST_ELEMENT_TEMPLATE(gt, "gt"),
	INST_ELEMENT_TEMPLATE(lt, "lt"),
	INST_ELEMENT_TEMPLATE(le, "le"),
	INST_ELEMENT_TEMPLATE(ge, "ge"),
	INST_ELEMENT_TEMPLATE(eql, "eql"),

	INST_ELEMENT_TEMPLATE(propsize, "propsize"),
	INST_ELEMENT_TEMPLATE(propmbeg, "propmbeg"),
	INST_ELEMENT_TEMPLATE(propmend, "propmend"),
	INST_ELEMENT_TEMPLATE(evval, "evval"),
	INST_ELEMENT_TEMPLATE(evmod, "evmod"),
	INST_ELEMENT_TEMPLATE(strlen, "strlen"),
	INST_ELEMENT_TEMPLATE(strcmp, "strcmp"),
	INST_ELEMENT_TEMPLATE(add, "add"),
	INST_ELEMENT_TEMPLATE(sub, "sub"),
	INST_ELEMENT_TEMPLATE(mul, "mul"),
	INST_ELEMENT_TEMPLATE(div, "div"),
	INST_ELEMENT_TEMPLATE(mod, "mod"),
	INST_ELEMENT_TEMPLATE(bor, "bor"),
	INST_ELEMENT_TEMPLATE(band, "band"),
	INST_ELEMENT_TEMPLATE(bxor, "bxor"),
	INST_ELEMENT_TEMPLATE(UCSval, "UCSval"),
	INST_ELEMENT_TEMPLATE(propadd, "propadd"),
	INST_ELEMENT_TEMPLATE(propcopy, "propcopy"),
	INST_ELEMENT_TEMPLATE(evchar, "evchar"),
	INST_ELEMENT_TEMPLATE(makechar, "makechar"),
	INST_ELEMENT_TEMPLATE(strref, "strref"),
	INST_ELEMENT_TEMPLATE(commit, "commit"),
	INST_ELEMENT_TEMPLATE(next, "next"),
	INST_ELEMENT_TEMPLATE(evtype, "evtype"),
	INST_ELEMENT_TEMPLATE(evmtext, "evmtext"),
	INST_ELEMENT_TEMPLATE(commit, "commit"),
	INST_ELEMENT_TEMPLATE(concat, "concat"),
	INST_ELEMENT_TEMPLATE(substr, "substr"),
	INST_ELEMENT_TEMPLATE(makeev, "makeev"),
	INST_ELEMENT_TEMPLATE(unroll, "unroll"),
	INST_ELEMENT_TEMPLATE(propdel, "propdel"),
	INST_ELEMENT_TEMPLATE(addmprop, "addmprop"),
	INST_ELEMENT_TEMPLATE(addmprop, "delmprop"),
	INST_ELEMENT_TEMPLATE(addmprop, "setmprop"),

	/* <propval> PROP IDX </propval> retrives
	   a value of property.  */
	INST_ELEMENT_TEMPLATE(propval, "propval"),

	/* <return> </return> returns a value of function. */
	{"return", PCE_return_parser, NULL,
	 PCE_arg_option, NULL,
	 &PCE_return_inst_template},

	/* exc(exception name) attribute */
	{"throw", PCE_throw_parser, NULL,
	 PCE_noarg_option, NULL,
	 &PCE_throw_inst_template},
	/* to(label name) attribute */
	{"go", PCE_go_parser, PCE_attr_go,
	 PCE_noarg_option, NULL, NULL},
	/* name(label name) attribute */
	{"label", PCE_label_parser, PCE_attr_label,
	 PCE_noarg_option, NULL, NULL},
	/* t(table name) attribute */
	{"tblkeymaxsize", PCE_tblkeymaxsize_parser, NULL,
	 EIMIL_element_EMPTY | EIMIL_element_multiple, NULL},
	{"tblvalmaxsize", PCE_tblvalmaxsize_parser, NULL,
	 EIMIL_element_EMPTY | EIMIL_element_multiple, NULL},
	{"tblref", PCE_tblref_parser, NULL,
	 PCE_arg_option, PCE_statement_template},

	/* p(property name) attribute */
	{"makeprop", PCE_makeprop_parser, NULL,
	 EIMIL_element_EMPTY| EIMIL_element_multiple, NULL},
	{"getmprop", PCE_getmprop_parser, NULL,
	 PCE_arg_option, PCE_statement_template},
	{"findmprop", PCE_findmprop_parser, NULL,
	 PCE_arg_option, PCE_statement_template},

	/* op(operation name) attribute */
	{"interact", PCE_interact_parser, NULL,
	 PCE_noarg_option, NULL},

	/* p(pattern name) attribute */
	{"match", PCE_match_parser, NULL,
	 PCE_arg_option, PCE_statement_template},

	/* to(EIMIL name) attribute */
	{"forward", PCE_forward_parser, PCE_attr_forward,
	 PCE_noarg_option, NULL},
	{"send", PCE_send_parser, PCE_attr_send,
	 PCE_arg_option, PCE_statement_template},

	/* v(variable name) attribute */
	{"set", PCE_set_parser, NULL,
	 PCE_arg_option, PCE_statement_template},
	/* f(function name) attribute */
	{"e", PCE_e_parser, NULL,
	 PCE_arg_option, PCE_statement_template},

	/* ends here */
	{NULL, NULL, NULL, 0, NULL}
};

static EIMIL_element_template
PCE_if_full_template[EIMIL_TEMPLATE_NUM(PCE_statement_template)
		     + EIMIL_TEMPLATE_NUM(PCE_if_else_template) - 1];
static EIMIL_element_template
PCE_try_full_template[EIMIL_TEMPLATE_NUM(PCE_statement_template)
		     + EIMIL_TEMPLATE_NUM(PCE_try_catch_template) - 1];

static EIMIL_attr_template
PCE_attr_defvar[] = {{"name", EIMIL_attr_REQUIRED, NULL},
		     {"type", EIMIL_attr_REQUIRED, NULL},
		     {"val", EIMIL_attr_NORMAL, "nil"},
		     {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_defun[] = {{"name", EIMIL_attr_REQUIRED, NULL},
		    {"type", EIMIL_attr_REQUIRED, NULL},
		    {"args", EIMIL_attr_NORMAL, ""},
		    {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_defpattern[] = {{"name", EIMIL_attr_REQUIRED, NULL},
			 {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_mnemonic[] = {{"c", EIMIL_attr_REQUIRED, NULL},
		       {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_pattern[] = {{"e", EIMIL_attr_REQUIRED, NULL},
		      {"v", EIMIL_attr_REQUIRED, NULL},
		      {NULL, 0, NULL}};

static EIMIL_element_template
PCE_defpattern_template[] = {
	{"mnemonic", PCE_mnemonic_parser, NULL,
	 PCE_arg_option, NULL},
	{"pattern", PCE_pattern_parser, PCE_attr_pattern,
	 PCE_noarg_option, NULL},
	{NULL, NULL, NULL, 0, NULL}
};

static EIMIL_attr_template
PCE_attr_deftable[] = {{"name", EIMIL_attr_REQUIRED, NULL},
		       {"from", EIMIL_attr_REQUIRED, NULL},
		       {"to", EIMIL_attr_REQUIRED, NULL},
		       {NULL, 0, NULL}};

static EIMIL_attr_template
PCE_attr_key[] = {{"code", EIMIL_attr_IMPLIED, NULL},
		  {"char", EIMIL_attr_IMPLIED, NULL},
		  {"mod", EIMIL_attr_IMPLIED, NULL},
		  {NULL, 0, NULL}};

static EIMIL_element_template
PCE_defkeymap_template[] = {
	{"key", PCE_key_parser, NULL,
	 PCE_arg_option, NULL},
	{NULL, NULL, NULL, 0, NULL}
};

static EIMIL_attr_template
PCE_attr_defkeymap[] = {{"name", EIMIL_attr_REQUIRED, NULL},
			{"to", EIMIL_attr_REQUIRED, NULL},
			{NULL, 0, NULL}};

static EIMIL_element_template
PCE_template[] = {{"defvar", PCE_defvar_parser,
		   PCE_attr_defvar,
		   PCE_arg_option, NULL},
		  {"defun", PCE_defun_parser,
		   PCE_attr_defun,
		   PCE_arg_option,
		   PCE_statement_template, &PCE_defun_inst_template},
		  {"deftable", PCE_deftable_parser,
		   PCE_attr_deftable,
		   PCE_arg_option, NULL},
		  {"defkeymap", PCE_defkeymap_parser,
		   PCE_attr_defkeymap,
		   PCE_arg_option,
		   PCE_defkeymap_template},
		  {"defpattern", PCE_defpattern_parser,
		   PCE_attr_defpattern,
		   PCE_arg_option,
		   PCE_defpattern_template},
		  {"main", PCE_main_parser, NULL,
		   EIMIL_element_single | EIMIL_allow_PCDATA,
		   PCE_statement_template, &PCE_main_inst_template},
		  {NULL, NULL, NULL, 0, NULL}};

EIMIL_element_template
PCE_docroot[] = {{"PCE", PCE_PCE_parser, NULL,
		  EIMIL_element_single, PCE_template},
		 {NULL, NULL, NULL, 0, NULL}};

/********************************************************************************
			    PCE precompilation part
*********************************************************************************/

enum PCE_inst {
	PCE_INVALID_INST = -1,
	PCE_NOP = 0,
	PCE_WHILE,
	PCE_GO,
	PCE_LABEL,
	PCE_UNDO,
	PCE_MARK_UNDO,
	PCE_TRY,
	PCE_THROW,
	PCE_IF,
	PCE_SELECT,
	PCE_TOGGLE_PREEDIT,
	PCE_TOGGLE_LOOKUP_CHOICE,
	PCE_KEYEVENTP,
	PCE_OR,
	PCE_AND,
	PCE_NOT,
	PCE_GT,
	PCE_LT,
	PCE_LE,
	PCE_GE,
	PCE_EQL,
	PCE_TBLKEYMAXSIZE,
	PCE_TBLVALMAXSIZE,
	PCE_PROPSIZE,
	PCE_PROPMBEG,
	PCE_PROPMEND,
	PCE_EVVAL,
	PCE_EVMOD,
	PCE_STRLEN,
	PCE_STRCMP,
	PCE_ADD,
	PCE_SUB,
	PCE_MUL,
	PCE_DIV,
	PCE_MOD,
	PCE_BOR,
	PCE_BAND,
	PCE_BXOR,
	PCE_UCSVAL,
	PCE_MAKEPROP,
	PCE_PROPADD,
	PCE_PROPCOPY,
	PCE_GETMPROP,
	PCE_FINDMPROP,
	PCE_EVCHAR,
	PCE_STRREF,
	PCE_MAKECHAR,
	PCE_INTERACT,
	PCE_MATCH,
	PCE_EVTYPE,
	PCE_EVMTEXT,
	PCE_CONCAT,
	PCE_SUBSTR,
	PCE_NEXT,
	PCE_FORWARD,
	PCE_SEND,
	PCE_MAKEEV,
	PCE_COMMIT,
	PCE_UNROLL,
	PCE_PROPDEL,
	PCE_ADDMPROP,
	PCE_DELMPROP,
	PCE_SETMPROP,
	PCE_SET,
	PCE_E,
	PCE_TBLREF
};

static EIMIL_symbol*
PCE_intern_soft(
    PCE_context *pctx,
    unsigned char *name
)
{
    EIMIL_symbol *psym;

    psym = EIMIL_intern_soft(pctx->pdic, name);
    if (!psym) {
	psym = EIMIL_intern_soft(pctx->ped->pdic, name);
    }
    return psym;
}

static EIMIL_symbol*
PCE_intern_soft_for_parser(
    PCE_parse_context *pcx,
    unsigned char *name
)
{
    EIMIL_symbol *psym;
    PCE_context *pctx = pcx->pctx;

    if (pcx->pdic)
	psym = EIMIL_intern_soft(pcx->pdic, name);
    else
	psym = NULL;
    if (!psym) {
	psym = EIMIL_intern_soft(pctx->pdic, name);
	if (!psym) {
	    psym = EIMIL_intern_soft(pctx->ped->pdic, name);
	}
    }
    return psym;
}

static EIMIL_symbol*
PCE_register_symbol(
    PCE_context *pctx,
    unsigned char *name,
    enum EIMIL_CATEGORY cat,
    enum EIMIL_TYPE type
)
{
    EIMIL_symbol *psym;
    psym = EIMIL_intern_soft(pctx->ped->pdic, name);
    if (!psym) {
	psym = EIMIL_register_symbol(pctx->ped,
				     pctx->pdic, name,
				     cat, type);
	if (psym) return psym;
    }
    EIMIL_set_error_pt(pctx->ped, NULL,
		       "Duplicated symbol name:%s.", name);
    return NULL;
}

static EIMIL_symbol*
PCE_get_variable(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    UTF8 *varname
)
{
    EIMIL_symbol *psym;

    psym = PCE_intern_soft_for_parser(pcx, varname);
    if (!psym) {
	PCE_set_error(ped, PCE_PARSE_UNKNOWN_SYMBOL_ERROR);
	return NULL;
    }
    if (psym->cat != EIMIL_CAT_VARIABLE) {
	PCE_set_error(ped, PCE_NOT_VARIABLE_ERROR);
	return NULL;
    }
    return psym;
}

static EIMIL_symbol*
PCE_get_function(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    UTF8 *varname
)
{
    EIMIL_symbol *psym;

    psym = PCE_intern_soft_for_parser(pcx, varname);
    if (!psym) {
	PCE_set_error(ped, PCE_PARSE_UNKNOWN_SYMBOL_ERROR);
	return NULL;
    }
    if (psym->cat != EIMIL_CAT_FUNCTION) {
	PCE_set_error(ped, PCE_NOT_FUNCTION_ERROR);
	return NULL;
    }
    return psym;
}

static EIMIL_symbol*
PCE_get_exception(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    UTF8 *varname
)
{
    EIMIL_symbol *psym;

    psym = PCE_intern_soft_for_parser(pcx, varname);
    if (!psym) {
	PCE_set_error(ped, PCE_PARSE_UNKNOWN_SYMBOL_ERROR);
	return NULL;
    }
    if (psym->cat != EIMIL_CAT_EXCEPTION) {
	PCE_set_error(ped, PCE_NOT_EXCEPTION_ERROR);
	return NULL;
    }
    return psym;
}

static EIMIL_symbol*
PCE_get_property(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    UTF8 *propname
)
{
    EIMIL_symbol *psym;

    psym = PCE_intern_soft_for_parser(pcx, propname);
    if (!psym) {
	PCE_set_error(ped, PCE_PARSE_UNKNOWN_SYMBOL_ERROR);
	return NULL;
    }
    if (psym->cat != EIMIL_CAT_PROPERTY) {
	PCE_set_error(ped, PCE_NOT_EXCEPTION_ERROR);
	return NULL;
    }
    return psym;
}

static PCE_code*
PCE_new_code_for_inst(PCE_EXEC_HANDLER h)
{
    PCE_code *p;

    p = (PCE_code*) malloc(sizeof(PCE_code));
    if (!p) return NULL;
    p->type = PCE_CODE_INST;
    p->val.h = h;
    p->parg = NULL;
    p->pnext = NULL;

    return p;
}

static PCE_code*
PCE_new_code_for_value(EIMIL_value *pv)
{
    PCE_code *p;

    p = (PCE_code*) malloc(sizeof(PCE_code));
    if (!p) return NULL;
    p->type = PCE_CODE_VALUE;
    p->val.pv = pv;
    p->parg = NULL;
    p->pnext = NULL;

    return p;
}

static PCE_code*
PCE_new_code_for_symbol(
    EIMIL_symbol *psym
)
{
    PCE_code *p;

    p = (PCE_code*) malloc(sizeof(PCE_code));
    if (!p) return NULL;
    p->type = PCE_CODE_SYMBOL;
    p->val.symid = psym->symbolid;
    p->parg = NULL;
    p->pnext = NULL;

    return p;
}

static PCE_code*
PCE_new_code_for_function(
    PCE_function *pf
)
{
    PCE_code *p;

    p = (PCE_code*) malloc(sizeof(PCE_code));
    if (!p) return NULL;
    p->type = PCE_CODE_FUNCTION;
    p->val.pf = pf;
    p->parg = NULL;
    p->pnext = NULL;

    return p;
}

static PCE_code*
PCE_new_code_for_etc(
    enum PCE_CODE_TYPE type
)
{
    PCE_code *p;

    p = (PCE_code*) malloc(sizeof(PCE_code));
    if (!p) return NULL;
    memset(p, 0, sizeof(PCE_code));
    p->type = type;

    return p;
}

static PCE_code*
PCE_new_code_for_jmp(
    enum PCE_CODE_TYPE jmptype,
    int id
)
{
    PCE_code *p;

    p = (PCE_code*) malloc(sizeof(PCE_code));
    if (!p) return NULL;
    memset(p, 0, sizeof(PCE_code));

    ASSERT((jmptype == PCE_CODE_UNRESOLVED_JMP)
	   || (jmptype == PCE_CODE_UNRESOLVED_COND_JMP)
	   || (jmptype == PCE_CODE_UNRESOLVED_COND_NOT_JMP));

    p->type = jmptype;
    p->val.to_labelid = id;

    return p;
}

static EIMIL_symbol*
PCE_lookup_symbol(
    PCE_context *pctx,
    PCE_code *pc
)
{
    EIMIL_symbol *psym;
    ASSERT((pc->type & PCE_CODE_SYMBOL) || (pc->type & PCE_CODE_DEFUN));

    if (pctx->pdic_f) {
	psym = EIMIL_lookup_symbol_internal(pctx->pdic_f, pc->val.symid);
	if (psym) return psym;
    }

    psym = EIMIL_lookup_symbol_internal(pctx->pdic, pc->val.symid);
    if (psym) return psym;

    psym = EIMIL_lookup_symbol_internal(pctx->ped->pdic, pc->val.symid);
    return psym;
}

static EIMIL_value*
PCE_symbol_value(
    PCE_context *pctx,
    PCE_code *pc
)
{
    EIMIL_symbol *psym = PCE_lookup_symbol(pctx, pc);
    ASSERT(psym && psym->cat == EIMIL_CAT_VARIABLE);
    return psym->obj.v.pv;
}

static EIMIL_dictionary*
PCE_new_dictionary(
    EIMIL_data *ped,
    PCE_context *pctx
)
{
    EIMIL_symbol *psym;
    EIMIL_dictionary *pdic;
    pdic = EIMIL_new_dictionary(PCE_DICTIONARY_DEFAULT_SIZE, 1);
    if (!pdic) return NULL;
    /* internal variables */

    psym = EIMIL_register_symbol(ped, pdic, "cev",
				 EIMIL_CAT_VARIABLE,
				 EIMIL_TYPE_EVENT);
    if (!psym) return NULL;
    psym->obj.v.type = EIMIL_TYPE_EVENT;
    psym->obj.v.constp = 1;
    psym->obj.v.pv = NULL;
    pctx->psym_cev = psym;

    psym = EIMIL_register_symbol(ped, pdic, "mapval",
				 EIMIL_CAT_VARIABLE,
				 EIMIL_TYPE_ANY);
    if (!psym) return NULL;
    psym->obj.v.type = EIMIL_TYPE_ANY;
    psym->obj.v.constp = 1;
    psym->obj.v.pv = NULL;

    pctx->pdic = pdic;

    return pdic;
}

static PCE_code*
PCE_reverse_code(
    PCE_code *p
)
{
    PCE_code *p1, *p2;

    if (!p) return NULL;

    p1 = NULL;
    for (;;) {
	p2 = p->pnext;
	p->pnext = p1;
	if (!p2) return p;
	p1 = p;
	p = p2;
    }

    /* notreached */
    return NULL;
}

static enum EIMIL_TYPE
PCE_get_type_from_name(
    UTF8 *name
)
{
    if (strcmp(name, "bool") == 0) return EIMIL_TYPE_BOOL;
    else if (strcmp(name, "number") == 0) return EIMIL_TYPE_NUMBER;
    else if (strcmp(name, "char") == 0) return EIMIL_TYPE_CHAR;
    else if (strcmp(name, "mtext") == 0) return EIMIL_TYPE_MTEXT;
    else if (strcmp(name, "event") == 0) return EIMIL_TYPE_EVENT;
    else if (strcmp(name, "prop") == 0) return EIMIL_TYPE_PROP;

    return EIMIL_TYPE_INVALID;
}

#define TYPE_NAME_LEN_MAX 64
static int
PCE_get_type_name(
    int type, char *buf, int len
)
{
    char *tn = buf;
    buf[0] = '\0';
    if (type & EIMIL_TYPE_ANY) {
	tn = "any type";
	if (strlen(tn) >= len) return 0;
	strcpy(buf, tn);
	return 1;
    }
    if (type & EIMIL_TYPE_BOOL) {
	tn = "bool";
	len -= strlen(tn);
	if (len <= 0) return 0;
	strcat(buf, tn);
    }
    if (type & EIMIL_TYPE_NUMBER) {
	if (*tn)
	    tn = " or number";
	else
	    tn = "number";
	len -= strlen(tn);
	if (len <= 0) return 0;
	strcat(buf, tn);
    }
    if (type & EIMIL_TYPE_CHAR) {
	if (*tn)
	    tn = " or char";
	else
	    tn = "char";
	len -= strlen(tn);
	if (len <= 0) return 0;
	strcat(buf, tn);
    }
    if (type & EIMIL_TYPE_MTEXT) {
	if (*tn)
	    tn = " or mtext";
	else
	    tn = "mtext";
	len -= strlen(tn);
	if (len <= 0) return 0;
	strcat(buf, tn);
    }
    if (type & EIMIL_TYPE_EVENT) {
	if (*tn)
	    tn = " or event";
	else
	    tn = "event";
	len -= strlen(tn);
	if (len <= 0) return 0;
	strcat(buf, tn);
    }
    if (type & EIMIL_TYPE_PROP) {
	if (*tn)
	    tn = " or prop";
	else
	    tn = "prop";
	len -= strlen(tn);
	if (len <= 0) return 0;
	strcat(buf, tn);
    }
    if (type & EIMIL_TYPE_NIL) {
	if (*tn)
	    tn = " or nil";
	else
	    tn = "nil";
	len -= strlen(tn);
	if (len <= 0) return 0;
	strcat(buf, tn);
    }

    return 1;
}

static void
PCE_type_mismatch_error(
    EIMIL_data *ped,
    int type,
    int req
)
{
    char mes_req[TYPE_NAME_LEN_MAX], mes_type[TYPE_NAME_LEN_MAX];

    if (!PCE_get_type_name(req, mes_req, sizeof(mes_req))) return;
    if (!PCE_get_type_name(type, mes_type, sizeof(mes_type))) return;
    EIMIL_set_error_pt(ped, NULL, "Argument type is wrong.(%s, %s)",
		       mes_req, mes_type);
}

static int
PCE_check_type(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    int type
)
{
    int i;
    int req;

    /* argument type */
    if (pcx->mode & PCE_IN_E) {
	PCE_function *pf = pcx->pf;
	if (pf->nargs <= pcx->idx) {
	    req = 0;
	} else {
	    PCE_funcproto *pfp = pf->pfp + pcx->idx;
	    req = pfp->type;
	}
    } else {
	for (i = 0; i <= pcx->idx; i++) {
	    if (i >= PCE_ARG_MAX) {
		req = 0;
		break;
	    }
	    req = pcx->required_type[i];
	    if (!req) break;
	    /* 
	       Note that EIMIL_TYPE_OPTION1 means it allows
	       arbitrary number of arguments.
	    */
	    if (req & EIMIL_TYPE_OPTION1) break;
	}
    }
    if (req == 0) {
	EIMIL_set_error_pt(ped, NULL, "Too many arguments.");
	return 0;
    }
    if ((req & EIMIL_TYPE_NIL) && (type == EIMIL_TYPE_NIL)) return 1;
    if ((req & EIMIL_TYPE_ANY) || (type & EIMIL_TYPE_ANY)) return 1;
    if ((req | type) == req) return 1;

    PCE_type_mismatch_error(ped, type, req);

    return 0;
}

static int
PCE_setup_arg(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    PCE_code **ppc
)
{
    int i, j, *pra;
    PCE_code *pc;

    /* Check the number of argument. */
    if (pcx->mode & PCE_IN_E) {
	i = pcx->pf->nargs;
    } else {
	pra = pcx->required_type;
	for (i = 0; i <= PCE_ARG_MAX; i++, pra++) {
	    if (!*pra) break;
	    if (*pra | EIMIL_TYPE_OPTION2) break;
	    if (*pra | EIMIL_TYPE_OPTION1) break;
	}
    }
    for (j = 0, pc = pcx->pc; pc; pc = pc->pnext, j++);
    if (j < i) {
	PCE_set_error(ped, PCE_TOO_FEW_ARGUMENTS_ERROR);
	return 0;
    }

    *ppc = PCE_reverse_code(pcx->pc);
    return 1;
}

static void
PCE_free_code(
    PCE_code *pc
)
{
    PCE_code *pc2;
    while (pc) {
	pc2 = pc->pnext;
	if (pc->parg) PCE_free_code(pc->parg);

	if (pc->type == PCE_CODE_VALUE) {
	    EIMIL_RMREF(*pc->val.pv);
	}
	free(pc);
	pc = pc2;
    }
}

static void
PCE_free_parse_context(
    PCE_parse_context *pcx
)
{
    PCE_parse_context *pcx2;

    if (pcx->pc)
	PCE_free_code(pcx->pc);
    pcx2 = pcx->pnext;
    free(pcx);
    if (pcx2) PCE_free_parse_context(pcx2);
}

/*****************************************
             PCE label
 *****************************************/

#define PCE_LABEL_INITIALI_ALLOC_SIZE 16;

static enum PCE_ERROR_CODE
PCE_initialize_labels(
    PCE_parse_context *pcx
)
{
    PCE_labelset *pls;
    pls = (PCE_labelset*) malloc(sizeof(PCE_labelset));
    if (!pls) return PCE_MEMORY_ERROR;
    memset(pls, 0, sizeof(*pls));
    pcx->plabels = pls;
    pls->pdic = EIMIL_new_dictionary(7, 0);
    if (!pls->pdic) {
	free(pls);
	return PCE_MEMORY_ERROR;
    }
    return PCE_SUCCESS;
}

static int
PCE_make_label(
    PCE_parse_context *pcx
)
{
    PCE_labelset *pls = pcx->plabels;
    PCE_label *pl;

    if (pls->allocednum <= pls->labelnum) {
	int newsize;
	if (pls->allocednum == 0) {
	    newsize = PCE_LABEL_INITIALI_ALLOC_SIZE;
	} else {
	    newsize = pls->allocednum * 2;
	}
	pl = (PCE_label*) realloc(pls->pl, sizeof(PCE_label) * newsize);
	if (!pl) return -1;
	pls->allocednum = newsize;
	pls->pl = pl;
    }

    pl = pls->pl + pls->labelnum;
    pl->id = pls->labelnum;
    pl->pc = NULL;
    pl->psym = NULL;
    pls->labelnum++;

    return pl->id;
}

static int
PCE_make_named_label(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    UTF8 *name
)
{
    int id;
    EIMIL_symbol *psym;
    PCE_labelset *pls = pcx->plabels;
    PCE_label *pl;

    id = PCE_make_label(pcx);
    if (id < 0) return id;
    pl = pls->pl + id;

    psym = EIMIL_register_symbol(ped, pls->pdic, name,
				 EIMIL_CAT_OPTIONAL,
				 EIMIL_TYPE_NONE);
    if (!psym) return -1;
    psym->obj.opt.optnum1 = id;
    pl->psym = psym;

    return id;
}

static int
PCE_lookup_named_label(
    PCE_parse_context *pcx,
    UTF8 *name
)
{
    int id;
    EIMIL_symbol *psym;
    PCE_labelset *pls = pcx->plabels;

    id = PCE_make_label(pcx);

    psym = EIMIL_intern_soft(pls->pdic, name);
    if (!psym) return -1;

    return psym->obj.opt.optnum1;
}

static void
PCE_mark_label(
    PCE_parse_context *pcx,
    int id,
    PCE_code *pc,
    int nextp
)
{
    PCE_labelset *pls = pcx->plabels;
    PCE_label *pl;
    ASSERT(id < pls->labelnum);
    pl = pls->pl + id;
    ASSERT(!pl->pc);
    pl->pc = pc;
    pl->nextp = nextp;
}

static void
PCE_mark_label_yet(
    PCE_parse_context *pcx,
    int id,
    PCE_code *pc,
    int nextp
)
{
    if (!((pcx->plabels->pl)[id].pc))
	PCE_mark_label(pcx, id, pc, nextp);
}

static enum PCE_ERROR_CODE
PCE_fixate_labels(
    EIMIL_data *ped,
    PCE_labelset *pls,
    PCE_code *pc_st
)
{
    enum PCE_ERROR_CODE r;
    PCE_label *pl;
    PCE_code *pc;

    for (pc = pc_st; pc; pc = pc->pnext) {
	if ((pc->type == PCE_CODE_UNRESOLVED_JMP)
	    || (pc->type == PCE_CODE_UNRESOLVED_COND_JMP)
	    || (pc->type == PCE_CODE_UNRESOLVED_COND_NOT_JMP)) {
	    ASSERT(pc->val.to_labelid < pls->labelnum);
	    pl = pls->pl + pc->val.to_labelid;
	    if (!pl->pc) {
		ASSERT(pl->psym);
		EIMIL_set_error(ped, "label:%s is not defined, but refered.",
				pl->psym->name);
		return PCE_LABEL_NOT_DEFINED_ERROR;
	    }
	    if (pc->type == PCE_CODE_UNRESOLVED_JMP)
		pc->type = PCE_CODE_JMP;
	    else if (pc->type == PCE_CODE_UNRESOLVED_COND_JMP)
		pc->type = PCE_CODE_COND_JMP;
	    else
		pc->type = PCE_CODE_COND_NOT_JMP;

	    if (pl->nextp)
		pc->val.pc_to = pl->pc->pnext;
	    else
		pc->val.pc_to = pl->pc;
	}
	if (pc->parg) {
	    r = PCE_fixate_labels(ped, pls, pc->parg);
	    if (r != PCE_SUCCESS) return r;
	}
    }
    return PCE_SUCCESS;
}

static enum PCE_ERROR_CODE
PCE_finalize_labels(
    EIMIL_data *ped,
    PCE_parse_context *pcx
)
{
    enum PCE_ERROR_CODE r;
    PCE_labelset *pls = pcx->plabels;

    r = PCE_fixate_labels(ped, pls, pcx->pc);
    if (r != PCE_SUCCESS) return r;

    EIMIL_free_dictionary_and_symbol(pls->pdic);
    free(pls->pl);
    free(pls);

    return PCE_SUCCESS;
}

/*****************************************
              literal
 *****************************************/

#define PCE_ischdec(c) (((c) >= '0' && (c) <= '9'))
#define PCE_chdec(c) ((c) - '0')
#define PCE_ischhex(c) (((c) >= '0' && (c) <= '9') \
                        || ((c) >= 'a' && (c) <= 'f') \
                        || ((c) >= 'A' && (c) <= 'F'))
#define PCE_chhex(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \
                      ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a') : ((c) - 'A'))

static enum PCE_ERROR_CODE
PCE_parse_literal(
    EIMIL_data *ped,
    UTF8 *pchars,
    EIMIL_value **ppv
)
{
    EIMIL_value *pv;
    if ((*pchars >= '1') && (*pchars <= '9')) {
	/* DEC NUMBER */
	int n = 0;
	for (; *pchars; pchars++) {
	    if (!PCE_ischdec(*pchars)) {
		PCE_set_error(ped, PCE_DEC_NUMBER_ERROR);
		return PCE_DEC_NUMBER_ERROR;
	    }
	    n = n * 10 + PCE_chdec(*pchars);
	    if (n > PCE_MAX_NUMBER) {
		PCE_set_error(ped, PCE_LITERAL_OVERFLOW);
		return PCE_LITERAL_OVERFLOW;
	    }
	}
	pv = EIMIL_construct_number(n);
    } else if (*pchars == '0') {
	int n;
	if (pchars[1] == '\0') {
	    n = 0;
	} else {
	    /* HEX NUMBER */
	    if (pchars[1] != 'x') {
		PCE_set_error(ped, PCE_HEX_NUMBER_ERROR);
		return PCE_HEX_NUMBER_ERROR;
	    }
	    pchars += 2;
	    n = 0;
	    for (; *pchars; pchars++) {
		if (!PCE_ischhex(*pchars)) {
		    PCE_set_error(ped, PCE_HEX_NUMBER_ERROR);
		    return PCE_HEX_NUMBER_ERROR;
		}
		n = n * 10 + PCE_chhex(*pchars);
		if (n > PCE_MAX_NUMBER) {
		    PCE_set_error(ped, PCE_LITERAL_OVERFLOW);
		    return PCE_LITERAL_OVERFLOW;
		}
	    }
	}
	pv = EIMIL_construct_number(n);
    } else if (*pchars == '\'') {
	/* CHAR */
	UTF32 ch;
	int n = EIMIL_convert_UTF8_to_UTF32char(pchars + 1, &ch);
	if ((pchars[n + 1] != '\'') || (pchars[n + 2] != 0)) {
	    PCE_set_error(ped, PCE_CHAR_LITERAL_ERROR);
	    return PCE_CHAR_LITERAL_ERROR;
	}
	pv = EIMIL_construct_char(ch);
    } else if (*pchars == '"') {
	/* MTEXT */
	UTF32 *pu32;
	int n = strlen(pchars);
	if (pchars[n - 1] != '"') {
	    PCE_set_error(ped, PCE_MTEXT_LITERAL_ERROR);
	    return PCE_MTEXT_LITERAL_ERROR;
	}
	pchars[n - 1] = 0;
	pu32 = EIMIL_convert_UTF8_to_UTF32(pchars + 1);
	pv = EIMIL_construct_mtext_from_UTF32(n - 2, pu32);
	free(pu32);
    } else {
	return PCE_PARSE_NOT_LITERAL;
    }
    if (!pv) {
	PCE_set_error(ped, PCE_MEMORY_ERROR);
	return PCE_MEMORY_ERROR;
    }

    *ppv = pv;
    return PCE_SUCCESS;
}

static enum PCE_ERROR_CODE
PCE_parse_token(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    UTF8 *pchars
)
{
    EIMIL_value *pv;
    enum PCE_ERROR_CODE r;
    r = PCE_parse_literal(ped, pchars, &pv);
    switch (r) {
      case PCE_SUCCESS:
      {
	  PCE_code *pc;
	  if (!PCE_check_type(ped, pcx, pv->type))
	      return PCE_WRONG_TYPE_ARGUMENT_ERROR;
	  pc = PCE_new_code_for_value(pv);
	  if (!pc) {
	      PCE_set_error(ped, PCE_MEMORY_ERROR);
	      return PCE_MEMORY_ERROR;
	  }
	  EIMIL_ADDREF(*pv);
	  PCE_ADD_CODE(pcx, pc);
	  return PCE_SUCCESS;
      }
      case PCE_PARSE_NOT_LITERAL:
      {
	  /* maybe VARIABLE */
	  PCE_code *pc;
	  EIMIL_symbol *psym;

	  psym = PCE_get_variable(ped, pcx, pchars);
	  if (!psym) return PCE_MEMORY_ERROR;
	  if (!PCE_check_type(ped, pcx, psym->obj.v.type))
	      return PCE_WRONG_TYPE_ARGUMENT_ERROR;
	  pc = PCE_new_code_for_symbol(psym);
	  if (!pc) return PCE_MEMORY_ERROR;
	  PCE_ADD_CODE(pcx, pc);

	  return PCE_SUCCESS;
      }
      default:
        break;
    }
    return r;
}

static enum EIMIL_TYPE
PCE_get_type_from_attrs(
    EIMIL_data *ped,
    EIMIL_attrs *patr
)
{
    UTF8 *name;
    enum EIMIL_TYPE type;

    for (; patr->name; patr++) {
	if (strcmp(patr->name, "type") == 0) {
	    if (!EIMIL_get_attr_nmtoken(patr->val, &name)) {
		type = EIMIL_TYPE_INVALID;
		break;
	    }
	    type = PCE_get_type_from_name(name);
	    free(name);
	    free(patr->name);
	    free(patr->val);
	    EIMIL_remove_attr(patr);
	    break;
	}
    }
    if (type == EIMIL_TYPE_INVALID) {
	EIMIL_set_error_pt(ped, NULL,
			   "Invalid type name:%s", patr->name);
    }

    return type;
}


/****************************************
           parser handlers
 ****************************************/

static PCE_parse_context*
PCE_parser_decl_start(
    EIMIL_data *ped,
    PCE_parse_context *pcx
)
{
    /* TODO */
    return NULL;
}

static PCE_parse_context*
PCE_new_parse_context(
    EIMIL_data *ped
)
{
    PCE_parse_context *pcnx;

    pcnx = (PCE_parse_context*) malloc(sizeof(PCE_parse_context));
    if (!pcnx) {
	EIMIL_set_out_of_memory(ped);
	return NULL;
    }
    memset(pcnx, 0, sizeof(PCE_parse_context));
    return pcnx;
}

static PCE_parse_context*
PCE_parser_generic_start(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    int type
)
{
    PCE_parse_context *pcnx;

    if (!PCE_check_type(ped, pcx, type)) return NULL;
    pcnx = PCE_new_parse_context(ped);
    if (!pcnx) return NULL;
    /* already done: pcnx->idx = 0; */
    pcnx->pnext = pcx;
    pcnx->result = type;
    pcnx->pctx = pcx->pctx;
    pcnx->mode = pcx->mode;
    pcnx->pdic = pcx->pdic;
    pcnx->pf = pcx->pf;
    pcnx->plabels = pcx->plabels;

    return pcnx;
}

static PCE_parse_context*
PCE_parser_generic_end( 
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    PCE_code *pc
)
{
    PCE_parse_context *pcx2;
    if (!pc) {
	EIMIL_set_out_of_memory(ped);
	PCE_free_parse_context(pcx);
	return NULL;
    }
    pcx2 = pcx->pnext;
    if (!PCE_setup_arg(ped, pcx, &pc->parg)) {
	PCE_free_parse_context(pcx);
	return NULL;
    }
    PCE_ADD_CODE(pcx2, pc);
    free(pcx);

    return pcx2;
}

static PCE_code*
PCE_insert_head_code(
    PCE_code *pc,
    PCE_code *pc_ins,
    int first_argp
)
{
    PCE_code *pc2, *pc_end, *pc_prevend;

    pc_end = pc_prevend = NULL;
    for (pc2 = pc; pc2; pc2 = pc2->pnext) {
	pc_prevend = pc_end;
	pc_end = pc2;
    }

    if (first_argp) {
	ASSERT(pc_end);
	pc_ins->parg = pc_end;
	if (pc_prevend)	pc_prevend->pnext = pc_ins;
	pc_ins->parg->pnext = NULL;
    } else {
	if (pc_end)
	    pc_end->pnext = pc_ins;
	else
	    return pc_ins;
    }

    return pc;
}

enum PCE_JMP_END_FLAG {
	PCE_UNCOND_JMP_END,
	PCE_COND_JMP_END,
	PCE_COND_NOT_JMP_END
};
static PCE_parse_context*
PCE_parser_jmp_end(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    enum PCE_JMP_END_FLAG condflag,
    int loopp
)
{
    int cflag;
    PCE_parse_context *pcx2;
    PCE_code *pc, *pc2;

    if (condflag == PCE_UNCOND_JMP_END) {
	pc = PCE_new_code_for_jmp(PCE_CODE_UNRESOLVED_JMP, pcx->labelid);
	cflag = 0;
    } else if (condflag == PCE_COND_JMP_END) {
	pc = PCE_new_code_for_jmp(PCE_CODE_UNRESOLVED_COND_JMP,
				  pcx->labelid);
	cflag = 1;
    } else {
	pc = PCE_new_code_for_jmp(PCE_CODE_UNRESOLVED_COND_NOT_JMP,
				  pcx->labelid);
	cflag = 1;
    }

    if (!pc) {
	EIMIL_set_out_of_memory(ped);
	PCE_free_parse_context(pcx);
	return NULL;
    }

    if (cflag && !pcx->pc) {
	PCE_set_error(ped, PCE_TOO_FEW_ARGUMENTS_ERROR);
	return NULL;
    }

    pc2 = PCE_insert_head_code(pcx->pc, pc, cflag);

    pcx2 = pcx->pnext;
    pc->pnext = pcx2->pc;

    if (loopp) {
	int loopid;
	PCE_code *pc_loop;

	loopid = PCE_make_label(pcx2);
	if (loopid < 0) return NULL;
	pc_loop = PCE_new_code_for_jmp(PCE_CODE_UNRESOLVED_JMP, loopid);
	if (!pc_loop) return NULL;
	pc_loop->pnext = pc2;
	pcx2->pc = pc_loop;
	PCE_mark_label(pcx2, loopid, pc, 0);
    } else {
	pcx2->pc = pc2;
    }

    free(pcx);

    return pcx2;
}

static PCE_parse_context*
PCE_parser_select_case_end(
    EIMIL_data *ped,
    PCE_parse_context *pcx
)
{
    PCE_parse_context *pcx2;
    PCE_code *pc_case_cond, *pc_jmp_to_tail;

    pcx2 = pcx->pnext;

    pc_case_cond = PCE_new_code_for_jmp(PCE_CODE_UNRESOLVED_COND_JMP,
					pcx->labelid2);
    pc_jmp_to_tail = PCE_new_code_for_jmp(PCE_CODE_UNRESOLVED_JMP,
					pcx2->labelid);
    if ((!pc_case_cond) || (!pc_jmp_to_tail)) {
	EIMIL_set_out_of_memory(ped);
	PCE_free_parse_context(pcx);
	return NULL;
    }

    PCE_ADD_CODE(pcx, pc_jmp_to_tail);

    {
	PCE_code *pc2, *pc_end, *pc_prevend;

	pc_end = pc_prevend = NULL;
	for (pc2 = pcx->pc; pc2; pc2 = pc2->pnext) {
	    pc_prevend = pc_end;
	    pc_end = pc2;
	}

	if (!(pc_end && pc_prevend)) {
	    PCE_set_error(ped, PCE_TOO_FEW_ARGUMENTS_ERROR);
	    return NULL;
	}

	pc_case_cond->parg = pc_end;
	pc_case_cond->pnext = pcx2->pc_head;
	pcx2->pc_head = pc_case_cond;

	pc_prevend->pnext = pcx2->pc;
	pcx2->pc = pcx->pc;
	PCE_mark_label(pcx2, pcx->labelid2, pc_prevend, 0);
    }

    free(pcx);
    return pcx2;
}

static PCE_parse_context*
PCE_parser_select_default_end(
    EIMIL_data *ped,
    PCE_parse_context *pcx
)
{
    PCE_parse_context *pcx2;
    PCE_code *pc_def_jmp;

    pcx2 = pcx->pnext;

    if (pcx->pc) {
	PCE_code *pc2, *pc_end;

	pc_def_jmp = PCE_new_code_for_jmp(PCE_CODE_UNRESOLVED_JMP,
					  pcx->labelid2);
	if (!pc_def_jmp) {
	    EIMIL_set_out_of_memory(ped);
	    return NULL;
	}
	pc_end = NULL;
	for (pc2 = pcx->pc; pc2; pc2 = pc2->pnext) {
	    pc_end = pc2;
	}
	PCE_mark_label(pcx2, pcx->labelid2, pc_end, 0);
	pc_end->pnext = pcx2->pc;
	pcx2->pc = pc_end;
	pcx2->mode |= PCE_SELECT_HAS_DEFAULT;
    } else {
	pc_def_jmp = PCE_new_code_for_jmp(PCE_CODE_UNRESOLVED_JMP,
					  pcx->labelid);
    }
    pc_def_jmp->pnext = pcx2->pc_head;
    pcx2->pc_head = pc_def_jmp;

    free(pcx);
    return pcx2;
}

static PCE_parse_context*
PCE_parser_select_end(
    EIMIL_data *ped,
    PCE_parse_context *pcx
)
{
    PCE_parse_context *pcx2;
    PCE_code *pc_cond_end;
    PCE_code *pc_block_end;

    pcx2 = pcx->pnext;

    pc_cond_end = pcx->pc_head;
    pc_block_end = pcx->pc;

    if (!(pcx->mode & PCE_SELECT_HAS_DEFAULT)) {
	PCE_code *pc = PCE_new_code_for_jmp(PCE_CODE_UNRESOLVED_JMP,
					    pcx->labelid);
	if (!pc) return NULL;
	pc->pnext = pc_cond_end;
	pc_cond_end = pc;
    }
    pc_block_end = PCE_insert_head_code(pc_block_end, pc_cond_end, 0);
    pcx2->pc = PCE_insert_head_code(pc_block_end, pcx2->pc, 0);
    PCE_mark_label_yet(pcx, pcx->labelid, pcx2->pc, 1);
    free(pcx);
    return pcx2;
}

/*
  attribute access
 */
static UTF8*
PCE_get_name_attribute(
    EIMIL_data *ped,
    EIMIL_attrs *patr
)
{
    UTF8 *name;
    for (; patr->name;patr++) {
	if (strcmp(patr->name, "name") == 0) {
	    if (!EIMIL_get_attr_nmtoken(patr->val, &name)) {
		EIMIL_set_error_pt(ped, NULL, "Invalid nmtoken in `name'");
		return NULL;
	    }
	    return name;
	}
    }
    return NULL;
}

enum PCE_VAL_TYPE {
	PCE_VAL_TYPE_INVALID,
	PCE_VAL_TYPE_NIL,
	PCE_VAL_TYPE_CONST,
	PCE_VAL_TYPE_INIT
};

static enum PCE_VAL_TYPE
PCE_get_val_type_attribute(
    EIMIL_data *ped,
    EIMIL_attrs *patr
)
{
    for (; patr->name; patr++) {
	if (strcmp(patr->name, "val") == 0) {
	    enum PCE_VAL_TYPE vt;
	    UTF8 *val;

	    if (!EIMIL_get_attr_nmtoken(patr->val, &val)) {
		EIMIL_set_error_pt(ped, NULL, "Unrecogized keyword in `val'");
		return PCE_VAL_TYPE_INVALID;
	    }
	    if (strcmp(val, "const") == 0) {
		vt = PCE_VAL_TYPE_CONST;
	    } else if (strcmp(val, "nil") == 0) {
		vt = PCE_VAL_TYPE_NIL;
	    } else if (strcmp(val, "init") == 0) {
		vt = PCE_VAL_TYPE_INIT;
	    } else {
		vt = PCE_VAL_TYPE_INVALID;
	    }
	    free(val);
	    return vt;
	}
    }
    return PCE_VAL_TYPE_INVALID;
}

#define PCE_ARGUMENT_DICTIONARY_SIZE 5
static int
PCE_get_funcproto_attribute(
    EIMIL_data *ped,
    EIMIL_attrs *patr,
    PCE_function **ppf
)
{
    for (; patr->name;patr++) {
	if (strcmp(patr->name, "args") == 0) {
	    enum EIMIL_TYPE type;
	    UTF8 *argname, *typename;
	    EIMIL_dictionary *pdic = NULL;
	    EIMIL_symbol *psym;
	    PCE_function *pf;
	    PCE_funcproto *pfp, *pfp2;
	    Ebyte *c = patr->val;
	    int i = 0;
	    int n = 0;

	    pfp2 = pfp = NULL;
	    while ((c = EIMIL_get_attr_nmtokens(c, &argname)) != NULL) {
		if (i >= n) {
		    n += 10;
		    pfp2 = (PCE_funcproto*) realloc(pfp2,
						    sizeof(PCE_funcproto) * n);
		    if (!pfp2) {
			PCE_set_error(ped, PCE_MEMORY_ERROR);
			return 0;
		    }
		    pfp = pfp2 + i;
		}
		typename = strchr(argname, ':');
		if (!typename) {
		    EIMIL_set_error_pt(ped, NULL,
				       "Function argument must specify type name.");
		    return 0;
		}
		*typename = '\0';
		typename++;
		type = PCE_get_type_from_name(typename);
		if (type == EIMIL_TYPE_INVALID) {
		    EIMIL_set_error_pt(ped, NULL,
				       "Invalid type name:%s.", typename);
		    return 0;
		}
		pfp->type = type;
		if (!pdic) {
		    pdic = EIMIL_new_dictionary(PCE_ARGUMENT_DICTIONARY_SIZE, 1);
		    if (!pdic) {
			PCE_set_error(ped, PCE_MEMORY_ERROR);
			return 0;
		    }
		}
		psym = EIMIL_register_symbol(ped, pdic, argname,
					     EIMIL_CAT_VARIABLE, type);
		pfp->psym = psym;
		pfp++;
		i++;
	    }
	    if (i > 0) {
		pfp = (PCE_funcproto*) realloc(pfp2,
					       sizeof(PCE_funcproto) * i);
		if (!pfp) {
		    PCE_set_error(ped, PCE_MEMORY_ERROR);
		    return 0;
		}
	    }		
	    pf = (PCE_function*) malloc(sizeof(PCE_function));
	    if (!pf) {
		PCE_set_error(ped, PCE_MEMORY_ERROR);
		return 0;
	    }
	    memset(pf, 0, sizeof(PCE_function));
	    pf->nargs = i;
	    pf->pdic = pdic;
	    pf->pfp = pfp;
	    pf->pc = NULL;
	    *ppf = pf;
	    return 1;
	}
    }
    return 0;
}

/*
  declaration.
 */

DEFINE_PARSER_METHOD(defvar)
{
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    switch (type) {
      case EIMIL_START_TAG:
      {
	  int constp = 0;
	  enum PCE_VAL_TYPE vt;
	  EIMIL_symbol *psym;
	  char *name;
	  
	  type = PCE_get_type_from_attrs(ped, patr);
	  if (type == EIMIL_TYPE_INVALID) return 0;
	  vt = PCE_get_val_type_attribute(ped, patr);
	  if (vt == PCE_VAL_TYPE_INVALID) return 0;
	  name = PCE_get_name_attribute(ped, patr);
	  if (!name) return 0;
	  if (vt == PCE_VAL_TYPE_CONST) {
	      constp = 1;
	  } else if (vt == PCE_VAL_TYPE_NIL) {
	      pcx->mode = PCE_SET_PARSER_CONTEXT_MODE(pcx,
						      PCE_DEFVAR_NIL);
	  }

	  psym = PCE_register_symbol(pcx->pctx, name,
				     EIMIL_CAT_VARIABLE,
				     type);
	  free(name);
	  if (!psym) return 0;
	  psym->publicp = 0;
	  psym->obj.v.constp = constp;
	  psym->obj.v.type = type;
	  psym->obj.v.pv = NULL;
	  pcx->psym = psym;
	  return 1;
      }
      case EIMIL_CHARDATA:
      {
	  EIMIL_value *pv;
	  EIMIL_symbol *psym = pcx->psym;
	  enum PCE_ERROR_CODE r;

	  if (pcx->mode & PCE_DEFVAR_DONE) {
	      PCE_set_error(ped, PCE_DEFVAR_DONE_ERROR);
	      return 0;
	  }
	  if (pcx->mode & PCE_DEFVAR_NIL) {
	      PCE_set_error(ped, PCE_DEFVAR_NIL_ERROR);
	      return 0;
	  }
	  r = PCE_parse_literal(ped, pchars, &pv);
	  if (r != PCE_SUCCESS) {
	      PCE_set_error(ped, r);
	      return 0;
	  }
	  if (psym->obj.v.type != pv->type) {
	      PCE_type_mismatch_error(ped, psym->obj.v.type, pv->type);
	      return 0;
	  }
	  
	  psym = pcx->psym;
	  ASSERT(psym);
	  pcx->psym->obj.v.pv = pv;
	  EIMIL_ADDREF(*pv);
	  pcx->mode = PCE_SET_PARSER_CONTEXT_MODE(pcx,
						  PCE_DEFVAR_DONE);
	  return 1;
      }

      case EIMIL_END_TAG:
       pcx->mode = PCE_RESET_PARSER_CONTEXT_MODE(pcx,
						 PCE_DEFVAR_NIL_ERROR
						 | PCE_DEFVAR_DONE);
       return 1;
       
      default:
       EIMIL_set_error_pt(ped, NULL, "Unknown error.");
    }

    return 0;
}

DEFINE_PARSER_METHOD(defun)
{
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;
    PCE_inst_template *pit = (PCE_inst_template*) ped->pcommon->ps.pcet->private;

    switch (type) {
      case EIMIL_START_TAG:
      {
	  PCE_function *pf;
	  EIMIL_symbol *psym;
	  char *name;
	  enum EIMIL_TYPE rettype;

	  rettype = PCE_get_type_from_attrs(ped, patr);
	  if (rettype == EIMIL_TYPE_INVALID) return 0;
	  name = PCE_get_name_attribute(ped, patr);
	  if (!name) return 0;
	  if (!PCE_get_funcproto_attribute(ped, patr, &pf)) return 0;
	  pf->rettype = rettype;
	  psym = PCE_register_symbol(pcx->pctx, name,
				     EIMIL_CAT_FUNCTION,
				     type);
	  free(name);
	  if (!psym)  return 0;
	  psym->publicp = 0;
	  psym->obj.f.type = type;
	  psym->obj.f.def = pf;

	  pcx = PCE_parser_generic_start(ped, pcx, pit->result);
	  if (!pcx) return 0;
	  PCE_SET_REQUIRED_TYPES(pcx, pit);
	  pcx->mode = PCE_NEW_PARSER_CONTEXT_MODE(pcx, PCE_IN_DEFUN);
	  pcx->psym = psym;
	  pcx->pdic = pf->pdic;
	  pcx->pf = pf;
	  *pprivate = pcx;
	  return 1;
      }
      case EIMIL_CHARDATA:
       EIMIL_set_error(ped, NULL, "The expression has no effect.");
       return 0;

      case EIMIL_END_TAG:
      {
	  EIMIL_symbol *psym = pcx->psym;
	  PCE_function *pf = (PCE_function*) psym->obj.f.def;
	  int symid;
	  symid = pcx->psym->symbolid;
	  pcx = PCE_parser_generic_end(ped, pcx,
				       PCE_new_code_for_etc(PCE_CODE_DEFUN));
	  pcx->pc->val.symid = symid;
	  if (!pcx) return 0;
	  pf->pc = pcx->pc;
	  return 1;
      }
       
      default:
       EIMIL_set_error_pt(ped, NULL, "Unknown error.");
    }

    return 0;
}

DEFINE_PARSER_METHOD(main)
{
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;
    PCE_inst_template *pit = (PCE_inst_template*) ped->pcommon->ps.pcet->private;

    switch (type) {
      case EIMIL_START_TAG:
      {
	  pcx = PCE_parser_generic_start(ped, pcx, pit->result);
	  if (!pcx) return 0;
	  PCE_SET_REQUIRED_TYPES(pcx, pit);
	  pcx->mode = PCE_NEW_PARSER_CONTEXT_MODE(pcx, PCE_IN_MAIN);
	  *pprivate = pcx;
	  return 1;
      }
      case EIMIL_CHARDATA:
       EIMIL_set_error(ped, NULL, "The expression has no effect.");
       return 0;

      case EIMIL_END_TAG:
      {
	  PCE_data *pd = pcx->pctx->ppce_data;
	  pcx = PCE_parser_generic_end(ped, pcx,
				       PCE_new_code_for_etc(PCE_CODE_MAIN));
	  if (!pcx) return 0;
	  pd->pcode = pcx->pc;
	  return 1;
      }
       
      default:
       EIMIL_set_error_pt(ped, NULL, "Unknown error.");
    }

    return 0;
}

DEFINE_PARSER_METHOD(deftable)
{
    /* TODO */
    return 1;
}

DEFINE_PARSER_METHOD(key)
{
    return 0;
}

DEFINE_PARSER_METHOD(defkeymap)
{
    /* TODO */
    return 1;
}

DEFINE_PARSER_METHOD(mnemonic)
{
    /* TODO */
    return 1;
}
DEFINE_PARSER_METHOD(pattern)
{
    /* TODO */
    return 1;
}
DEFINE_PARSER_METHOD(defpattern)
{
    /* TODO */
    return 1;
}

/*
  generic parser
 */

static int
PCE_generic_inst_parser(
    EIMIL_data *ped,
    EIMIL_attrs *patr,
    enum EIMIL_TAG_TYPE type,
    UTF8 *pchars,
    void **pprivate
)
{
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;
    PCE_inst_template *pit = (PCE_inst_template*) ped->pcommon->ps.pcet->private;

    switch (type) {
      case EIMIL_START_TAG:
       pcx = PCE_parser_generic_start(ped, pcx, pit->result);
       if (!pcx) return 0;
       PCE_SET_REQUIRED_TYPES(pcx, pit);
       if ((pit->newmode & PCE_ONLY_IN_MAIN_STATEMENT)
	   && !(pcx->mode & PCE_IN_MAIN)) {
	   EIMIL_set_error_pt(ped, NULL, "`%s' element must be in `main' element.",
			      ped->pcommon->ps.pcet->name);
	   return 0;
       }
       pcx->mode = PCE_NEW_PARSER_CONTEXT_MODE(pcx, pit->newmode);
       *pprivate = pcx;
       return 1;

      case EIMIL_CHARDATA:
       if (PCE_parse_token(ped, pcx, pchars) == PCE_SUCCESS) return 1;
       return 0;

      case EIMIL_END_TAG:
       if (pit->code_type == PCE_CODE_INST)
	   pcx = PCE_parser_generic_end(ped, pcx, PCE_new_code_for_inst(pit->handler));
       else
	   pcx = PCE_parser_generic_end(ped, pcx, PCE_new_code_for_etc(pit->code_type));
       if (!pcx) return 0;
       return 1;

      default:
       EIMIL_set_error_pt(ped, NULL, "Unknown error.");
    }

    return 0;
}

/*
  return element
 */

DEFINE_PARSER_METHOD(return)
{
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    if (type == EIMIL_START_TAG) {
	int r;
	if (!(pcx->mode & PCE_IN_DEFUN)) {
	    PCE_set_error(ped, PCE_PARSE_RETURN_IN_DEFUN_ERROR);
	}
	r = PCE_generic_inst_parser(ped, patr, type, pchars, pprivate);
	ASSERT(pcx->pf);
	pcx->required_type[0] = pcx->pf->rettype;
	return r;
    }
    return PCE_generic_inst_parser(ped, patr, type, pchars, pprivate);
}

/*
  flow control
 */

DEFINE_PARSER_METHOD(if)
{
    int id;
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    switch (type) {
      case EIMIL_START_TAG:
       id = PCE_make_label(pcx);
       if (id < 0) return 0;
       pcx = PCE_parser_generic_start(ped, pcx, EIMIL_TYPE_NONE);
       if (!pcx) return 0;
       pcx->required_type[0] = EIMIL_TYPE_ANY;
       pcx->required_type[1] = EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1;
       pcx->mode = PCE_NEW_PARSER_CONTEXT_MODE(pcx, PCE_IN_IF);
       pcx->labelid = pcx->labelid2 = id;
       *pprivate = pcx;
       return 1;

      case EIMIL_CHARDATA:
       if (PCE_parse_token(ped, pcx, pchars) == PCE_SUCCESS) return 1;
       return 0;

      case EIMIL_END_TAG:
      {
	  int st_label_id = pcx->labelid2;
	  pcx = PCE_parser_jmp_end(ped, pcx, PCE_COND_NOT_JMP_END, 0);
	  if (!pcx) return 0;
	  PCE_mark_label_yet(pcx, st_label_id, pcx->pc, 1);
	  return 1;
      }
      default:
        break;
    }

    return 0;
}

DEFINE_PARSER_METHOD(else)
{
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    switch (type) {
      case EIMIL_START_TAG:
      {
	  int id;
	  PCE_code *pc;

	  id = PCE_make_label(pcx);
	  if (id < 0) return 0;
	  pc = PCE_new_code_for_jmp(PCE_CODE_UNRESOLVED_JMP, id);
	  if (!pc) return 0;
	  PCE_ADD_CODE(pcx, pc);
	  PCE_mark_label(pcx, pcx->labelid, pc, 1);
	  pcx->labelid2 = id;
	  return 1;
      }

      case EIMIL_CHARDATA:
       if (PCE_parse_token(ped, pcx, pchars) == PCE_SUCCESS) return 1;
       return 0;

      case EIMIL_END_TAG:
       return 1;

      default:
       break;
    }

    return 0;
}

DEFINE_PARSER_METHOD(while)
{
    int id;
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    switch (type) {
      case EIMIL_START_TAG:
       id = PCE_make_label(pcx);
       if (id < 0) return 0;
       pcx = PCE_parser_generic_start(ped, pcx, EIMIL_TYPE_NONE);
       if (!pcx) return 0;
       pcx->required_type[0] = EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1;
       pcx->mode = PCE_NEW_PARSER_CONTEXT_MODE(pcx, PCE_IN_IF);
       pcx->labelid = id;
       *pprivate = pcx;
       return 1;

      case EIMIL_CHARDATA:
       if (PCE_parse_token(ped, pcx, pchars) == PCE_SUCCESS) return 1;
       return 0;

      case EIMIL_END_TAG:
      {
	  int st_label_id = pcx->labelid;
	  pcx = PCE_parser_jmp_end(ped, pcx, PCE_COND_NOT_JMP_END, 1);
	  if (!pcx) return 0;
	  PCE_mark_label_yet(pcx, st_label_id, pcx->pc, 1);
	  return 1;
      }

      default:
        break;
    }

    return 0;
}

DEFINE_PARSER_METHOD(case)
{
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    switch (type) {
      case EIMIL_START_TAG:
      {
	  int id;

	  id = PCE_make_label(pcx);
	  if (id < 0) return 0;
	  pcx = PCE_parser_generic_start(ped, pcx, EIMIL_TYPE_NONE);
	  if (!pcx) return 0;
	  pcx->required_type[0] = EIMIL_TYPE_ANY;
	  pcx->required_type[1] = EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1;
	  pcx->mode = PCE_NEW_PARSER_CONTEXT_MODE(pcx, PCE_IN_SELECT);
	  pcx->labelid2 = id;
	  *pprivate = pcx;
	  return 1;
      }

      case EIMIL_CHARDATA:
       if (PCE_parse_token(ped, pcx, pchars) == PCE_SUCCESS) return 1;
       return 0;

      case EIMIL_END_TAG:
       pcx = PCE_parser_select_case_end(ped, pcx);
       if (!pcx) return 0;
       return 1;
      default:
       break;
    }

    return 0;
}

DEFINE_PARSER_METHOD(default)
{
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    switch (type) {
      case EIMIL_START_TAG:
      {
	  int id;

	  id = PCE_make_label(pcx);
	  if (id < 0) return 0;
	  pcx = PCE_parser_generic_start(ped, pcx, EIMIL_TYPE_NONE);
	  if (!pcx) return 0;
	  pcx->required_type[0] = EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1;
	  pcx->mode = PCE_NEW_PARSER_CONTEXT_MODE(pcx, PCE_IN_SELECT);
	  pcx->labelid2 = id;
	  *pprivate = pcx;
	  return 1;
      }

      case EIMIL_CHARDATA:
       if (PCE_parse_token(ped, pcx, pchars) == PCE_SUCCESS) return 1;
       return 0;

      case EIMIL_END_TAG:
       pcx = PCE_parser_select_default_end(ped, pcx);
       if (!pcx) return 0;
       return 1;

      default:
       break;
    }

    return 0;
}

DEFINE_PARSER_METHOD(select)
{
    int id;
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    switch (type) {
      case EIMIL_START_TAG:
       id = PCE_make_label(pcx); /* label for the tail.  */
       if (id < 0) return 0;
       pcx = PCE_parser_generic_start(ped, pcx, EIMIL_TYPE_NONE);
       if (!pcx) return 0;
       pcx->required_type[0] = EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1;
       pcx->mode = PCE_NEW_PARSER_CONTEXT_MODE(pcx, PCE_IN_SELECT);
       pcx->labelid = id;
       pcx->pc_head = NULL; /* Used for the head jmp table(s). */
       *pprivate = pcx;
       return 1;

      case EIMIL_CHARDATA:
       return 0;

      case EIMIL_END_TAG:
      {
	  pcx = PCE_parser_select_end(ped, pcx);
	  if (!pcx) return 0;
	  return 1;
      }
      default:
       break;
    }

    return 0;
}

/*
  SEH
*/

DEFINE_PARSER_METHOD(try)
{
    /* TODO */
    return 1;
}

/*
  inst with attr.
*/

/* exc attr */
static EIMIL_symbol*
PCE_attr_get_exception_symbol(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    EIMIL_attrs *patr
)
{
    for (; patr->name;patr++) {
	if (strcmp(patr->name, "exc") == 0) {
	    EIMIL_symbol *psym;
	    UTF8 *name;
	    if (!EIMIL_get_attr_nmtokens(patr->val, &name)) {
		EIMIL_set_error_pt(ped, NULL, "Unrecogized keyword in `v'");
		return NULL;
	    }
	    psym = PCE_get_exception(ped, pcx, name);
	    free(name);
	    if (!psym) return NULL;
	    return psym;
	}
    }
    return NULL;
}
DEFINE_PARSER_METHOD(throw)
{
    if (type != EIMIL_EMPTY_TAG) return 0;

    {
	PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

	EIMIL_symbol *psym;
	PCE_code *pc, *pc_arg;
	  
	psym = PCE_attr_get_exception_symbol(ped, pcx, patr);

	pc_arg = PCE_new_code_for_symbol(pcx->psym);
	if (!pc_arg) {
	    PCE_set_error(ped, PCE_MEMORY_ERROR);
	    return 0;
	}
	pc = PCE_new_code_for_inst(PCE_throw_exec);
	if (!pc) {
	    PCE_set_error(ped, PCE_MEMORY_ERROR);
	    return 0;
	}
	pc->parg = pc_arg;
	PCE_ADD_CODE(pcx, pc);
    }

    return 1;
}

DEFINE_PARSER_METHOD(catch)
{
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    switch (type) {
      case EIMIL_START_TAG:
      {
	  EIMIL_symbol *psym;
	  
	  psym = PCE_attr_get_exception_symbol(ped, pcx, patr);
	  pcx = PCE_parser_generic_start(ped, pcx, EIMIL_TYPE_NONE);
	  if (!pcx) return 0;
	  pcx->required_type[0] = EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1;
	  pcx->mode = PCE_NEW_PARSER_CONTEXT_MODE(pcx, 0);
	  pcx->psym = psym;
	  *pprivate = pcx;
	  return 1;
      }
      case EIMIL_CHARDATA:
       if (PCE_parse_token(ped, pcx, pchars) == PCE_SUCCESS) return 1;
       return 0;

      case EIMIL_END_TAG:
      {
	  PCE_code *pc = PCE_new_code_for_symbol(pcx->psym);
	  if (!pc) {
	      PCE_set_error(ped, PCE_MEMORY_ERROR);
	      return 0;
	  }
	  pcx = PCE_parser_generic_end(ped, pcx,
				       PCE_new_code_for_inst(PCE_catch_exec));
	  /* Insert EIMIL_exception symbol
	     to the first argument of `catch'.  */
	  pc->pnext = pcx->pc->parg;
	  pcx->pc->parg = pc;
	  if (!pcx) return 0;
	  return 1;
      }
      default:
      break;
    }

    return 0;
}

/* exc attr */
static UTF8*
PCE_attr_get_label_symbol(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    EIMIL_attrs *patr
)
{
    for (; patr->name;patr++) {
	if (strcmp(patr->name, "to") == 0) {
	    UTF8 *name;
	    if (!EIMIL_get_attr_nmtokens(patr->val, &name)) {
		EIMIL_set_error_pt(ped, NULL, "Unrecogized keyword in `v'");
		return NULL;
	    }
	    return name;
	}
    }
    return NULL;
}
DEFINE_PARSER_METHOD(go)
{
    int id;
    UTF8* labelname;
    PCE_code *pc;
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    ASSERT(type == EIMIL_EMPTY_TAG);

    if (!(pcx->mode & PCE_IN_MAIN)) {
	EIMIL_set_error_pt(ped, NULL, "`go' element must be in `main' element.");
	return 0;
    }

    labelname = PCE_attr_get_label_symbol(ped, pcx, patr);
    if (!labelname) return 0;

    id = PCE_lookup_named_label(pcx, labelname);
    if (id < 0) {
	id = PCE_make_named_label(ped, pcx, labelname);
	if (id < 0) {
	    free(labelname);
	    return 0;
	}
    }
    free(labelname);
    pc = PCE_new_code_for_jmp(PCE_CODE_UNRESOLVED_JMP, id);
    PCE_ADD_CODE(pcx, pc);

    return 1;
}

DEFINE_PARSER_METHOD(label)
{
    int id;
    UTF8* labelname;
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    ASSERT(type == EIMIL_EMPTY_TAG);

    if (!(pcx->mode & PCE_IN_MAIN)) {
	EIMIL_set_error_pt(ped, NULL, "`label' element must be in `main' element.");
	return 0;
    }

    labelname = PCE_attr_get_label_symbol(ped, pcx, patr);
    if (!labelname) return 0;

    id = PCE_lookup_named_label(pcx, labelname);
    if (id < 0) {
	id = PCE_make_named_label(ped, pcx, labelname);
	if (id < 0) return 0;
    }
    PCE_mark_label(pcx, id, pcx->pc, 1);

    return 1;
}

DEFINE_PARSER_METHOD(tblkeymaxsize)
{
    return 1;
}

DEFINE_PARSER_METHOD(tblvalmaxsize)
{
    return 1;
}

DEFINE_PARSER_METHOD(tblref)
{
    return 1;
}

/* exc attr */
static EIMIL_symbol*
PCE_attr_get_property_symbol(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    EIMIL_attrs *patr
)
{
    for (; patr->name;patr++) {
	if (strcmp(patr->name, "p") == 0) {
	    EIMIL_symbol *psym;
	    UTF8 *name;
	    if (!EIMIL_get_attr_nmtokens(patr->val, &name)) {
		EIMIL_set_error_pt(ped, NULL, "Unrecogized keyword in `p'");
		return NULL;
	    }
	    psym = PCE_get_property(ped, pcx, name);
	    return psym;
	}
    }
    return NULL;
}
DEFINE_PARSER_METHOD(makeprop)
{
    if (type != EIMIL_EMPTY_TAG) return 0;

    {
	PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

	EIMIL_symbol *psym;
	PCE_code *pc, *pc_arg;
	  
	psym = PCE_attr_get_property_symbol(ped, pcx, patr);

	pc_arg = PCE_new_code_for_symbol(pcx->psym);
	if (!pc_arg) {
	    PCE_set_error(ped, PCE_MEMORY_ERROR);
	    return 0;
	}
	pc = PCE_new_code_for_inst(PCE_makeprop_exec);
	if (!pc) {
	    PCE_set_error(ped, PCE_MEMORY_ERROR);
	    return 0;
	}
	pc->parg = pc_arg;
	PCE_ADD_CODE(pcx, pc);
    }

    return 1;
}

DEFINE_PARSER_METHOD(getmprop)
{
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    switch (type) {
      case EIMIL_START_TAG:
      {
	  EIMIL_symbol *psym;
	  
	  psym = PCE_attr_get_property_symbol(ped, pcx, patr);
	  pcx = PCE_parser_generic_start(ped, pcx, EIMIL_TYPE_NONE);
	  if (!pcx) return 0;
	  pcx->required_type[0] = EIMIL_TYPE_MTEXT;
	  pcx->required_type[1] = EIMIL_TYPE_NUMBER;
	  pcx->mode = PCE_NEW_PARSER_CONTEXT_MODE(pcx, 0);
	  pcx->psym = psym;
	  *pprivate = pcx;
	  return 1;
      }
      case EIMIL_CHARDATA:
       if (PCE_parse_token(ped, pcx, pchars) == PCE_SUCCESS) return 1;
       return 0;

      case EIMIL_END_TAG:
      {
	  PCE_code *pc = PCE_new_code_for_symbol(pcx->psym);
	  if (!pc) {
	      PCE_set_error(ped, PCE_MEMORY_ERROR);
	      return 0;
	  }
	  pcx = PCE_parser_generic_end(ped, pcx,
				       PCE_new_code_for_inst(PCE_getmprop_exec));
	  /* Insert EIMIL_exception symbol
	     to the first argument of `catch'.  */
	  pc->pnext = pcx->pc->parg;
	  pcx->pc->parg = pc;
	  if (!pcx) return 0;
	  return 1;
      }
      default:
        break;
    }

    return 0;
}

DEFINE_PARSER_METHOD(findmprop)
{
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    switch (type) {
      case EIMIL_START_TAG:
      {
	  EIMIL_symbol *psym;
	  
	  psym = PCE_attr_get_property_symbol(ped, pcx, patr);
	  pcx = PCE_parser_generic_start(ped, pcx, EIMIL_TYPE_NONE);
	  if (!pcx) return 0;
	  pcx->required_type[0] = EIMIL_TYPE_MTEXT;
	  pcx->required_type[1] = EIMIL_TYPE_NUMBER;
	  pcx->mode = PCE_NEW_PARSER_CONTEXT_MODE(pcx, 0);
	  pcx->psym = psym;
	  *pprivate = pcx;
	  return 1;
      }
      case EIMIL_CHARDATA:
       if (PCE_parse_token(ped, pcx, pchars) == PCE_SUCCESS) return 1;
       return 0;

      case EIMIL_END_TAG:
      {
	  PCE_code *pc = PCE_new_code_for_symbol(pcx->psym);
	  if (!pc) {
	      PCE_set_error(ped, PCE_MEMORY_ERROR);
	      return 0;
	  }
	  pcx = PCE_parser_generic_end(ped, pcx,
				       PCE_new_code_for_inst(PCE_findmprop_exec));
	  /* Insert EIMIL_exception symbol
	     to the first argument of `catch'.  */
	  pc->pnext = pcx->pc->parg;
	  pcx->pc->parg = pc;
	  if (!pcx) return 0;
	  return 1;
      }
      default:
        break;
    }

    return 0;
}

DEFINE_PARSER_METHOD(interact)
{
    return 1;
}

DEFINE_PARSER_METHOD(match)
{
    return 1;
}

DEFINE_PARSER_METHOD(forward)
{
    return 1;
}

DEFINE_PARSER_METHOD(send)
{
    return 1;
}

/* v attr */
static EIMIL_symbol*
PCE_attr_get_variable_symbol(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    EIMIL_attrs *patr
)
{
    for (; patr->name;patr++) {
	if (strcmp(patr->name, "v") == 0) {
	    EIMIL_symbol *psym;
	    UTF8 *name;
	    if (!EIMIL_get_attr_nmtokens(patr->val, &name)) {
		EIMIL_set_error_pt(ped, NULL, "Unrecogized keyword in `v'");
		return NULL;
	    }
	    psym = PCE_get_variable(ped, pcx, name);
	    free(name);
	    if (!psym) return NULL;
	    return psym;
	}
    }
    return NULL;
}

DEFINE_PARSER_METHOD(set)
{
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    switch (type) {
      case EIMIL_START_TAG:
      {
	  EIMIL_symbol *psym;
	  
	  psym = PCE_attr_get_variable_symbol(ped, pcx, patr);
	  if (!psym) {
	      EIMIL_set_error_pt(ped, NULL,
				 "invalid `v' attribute in `set' element.");
	      return 0;
	  }
	  pcx = PCE_parser_generic_start(ped, pcx, psym->obj.v.type);
	  if (!pcx) return 0;
	  pcx->required_type[0] = psym->obj.v.type | EIMIL_TYPE_NIL;
	  pcx->mode = PCE_NEW_PARSER_CONTEXT_MODE(pcx, 0);
	  pcx->psym = psym;
	  *pprivate = pcx;
	  return 1;
      }
      case EIMIL_CHARDATA:
       if (PCE_parse_token(ped, pcx, pchars) == PCE_SUCCESS) return 1;
       return 0;

      case EIMIL_END_TAG:
      {
	  PCE_code *pc = PCE_new_code_for_symbol(pcx->psym);
	  if (!pc) {
	      PCE_set_error(ped, PCE_MEMORY_ERROR);
	      return 0;
	  }
	  PCE_ADD_CODE(pcx, pc);
	  pcx = PCE_parser_generic_end(ped, pcx,
				       PCE_new_code_for_inst(PCE_set_exec));
	  if (!pcx) return 0;
	  return 1;
      }
      default:
          break;
    }

    return 0;
}

/* f attr */
static EIMIL_symbol*
PCE_attr_get_function_symbol(
    EIMIL_data *ped,
    PCE_parse_context *pcx,
    EIMIL_attrs *patr
)
{
    for (; patr->name;patr++) {
	if (strcmp(patr->name, "f") == 0) {
	    EIMIL_symbol *psym;
	    UTF8 *name;
	    if (!EIMIL_get_attr_nmtokens(patr->val, &name)) {
		EIMIL_set_error_pt(ped, NULL, "Unrecogized keyword in `v'");
		return NULL;
	    }
	    psym = PCE_get_function(ped, pcx, name);
	    free(name);
	    if (!psym) return NULL;
	    return psym;
	}
    }
    return NULL;
}
DEFINE_PARSER_METHOD(e)
{
    PCE_parse_context *pcx = (PCE_parse_context*) *pprivate;

    switch (type) {
      case EIMIL_START_TAG:
      {
	  EIMIL_symbol *psym;
	  PCE_function *pf;

	  psym = PCE_attr_get_function_symbol(ped, pcx, patr);
	  pf = (PCE_function*) psym->obj.f.def;
	  pcx = PCE_parser_generic_start(ped, pcx, pf->rettype);
	  if (!pcx) return 0;
	  pcx->mode = PCE_NEW_PARSER_CONTEXT_MODE(pcx, PCE_IN_E);
	  pcx->pf = pf;
	  *pprivate = pcx;
	  return 1;
      }
      case EIMIL_CHARDATA:
       if (PCE_parse_token(ped, pcx, pchars) == PCE_SUCCESS) return 1;
       return 0;

      case EIMIL_END_TAG:
       pcx = PCE_parser_generic_end(ped, pcx,
				    PCE_new_code_for_function(pcx->pf));
       if (!pcx) return 0;
       return 1;

      default:
       break;
    }

    return 0;
}

/*
  PCE root element
*/

DEFINE_PARSER_METHOD(PCE)
{
    PCE_parse_context *pcx;

    if (type == EIMIL_START_TAG) {
	PCE_context *pctx = (PCE_context*) *pprivate;

	pcx = PCE_new_parse_context(ped);
	if (!pcx) return 0;
	if (PCE_initialize_labels(pcx) != PCE_SUCCESS) return 0;
	pcx->pctx = pctx;
	pcx->required_type[0] = EIMIL_TYPE_ANY | EIMIL_TYPE_OPTION1;
	*pprivate = pcx;
	return 1;
    } else {
	pcx = (PCE_parse_context*) *pprivate;
	ASSERT(!pcx->pnext);
	PCE_finalize_labels(ped, pcx);
	/*
	  Almost all of the work on pcx->pctx should be done in the
	  subelement parsers.  Thus currently do nothing in the root parser.
	*/
	free(pcx);
	return 1;
    }

    return 0;
}

/****************************************
            factory method
 ****************************************/

static void
PCE_SEH_free(
    PCE_SEH_block *pseh
);

static void
PCE_destruct_context(
    PCE_context *pctx
)
{
    EIMIL_free_dictionary(pctx->pdic);
    {
	PCE_SEH_block *pseh1, *pseh2;
	for (pseh1 = pctx->pseh; pseh1; ) {
	    pseh2 = pseh1->pnext;
	    PCE_SEH_free(pseh1);
	    pseh1 = pseh2;
	}
    }
    free(pctx);
}

static void
PCE_destruct_data(
    PCE_data *pd
)
{
    PCE_free_code(pd->pcode);
    free(pd);
}


static void*
PCE_handler(
    enum EIMIL_ENGINE_COMMAND m,
    EIMIL_data *ped,
    void *class_private,
    void *handle_private
)
{
    PCE_data *pd = (PCE_data*) class_private;
    PCE_context *ph = (PCE_context*) handle_private;

    switch (m) {
      case EIMIL_ENGINE_INSTANCIATE:
       pd = (PCE_data*) malloc(sizeof(PCE_data));
       if (!pd) return NULL;
       memset(pd, 0, sizeof(*pd));
       return pd;
       
      case EIMIL_ENGINE_DUPLICATE:
       if (!ph) {
	   ph = (PCE_context*) malloc(sizeof(PCE_context));
	   if (!ph) return NULL;
	   memset(ph, 0, sizeof(PCE_context));
	   ph->ped = ped;
	   PCE_new_dictionary(ped, ph);
	   ph->ppce_data = pd;
	   if (!ph->pdic) {
	       free(ph);
	       return NULL;
	   }
	   return ph;
       } else {
	   PCE_context *pnh;
	   pnh = (PCE_context*) malloc(sizeof(PCE_context));
	   if (!pnh) return NULL;
	   memset(pnh, 0, sizeof(PCE_context));
	   pnh->ped = ped;
	   pnh->pdic = EIMIL_duplicate_dictionary(ph->pdic);
	   pnh->ppce_data = ph->ppce_data;
	   if (!pnh->pdic) {
	       free(pnh);
	       return NULL;
	   }
	   return pnh;
       }

      case EIMIL_ENGINE_DESTRUCT:
       PCE_destruct_context(ph);
       return NULL;
       
      case EIMIL_ENGINE_UNINSTANCIATE:
       PCE_destruct_data(pd);
       return NULL;
    }

    return NULL;
}

/********************************************************************************
			     PCE execution part
*********************************************************************************/

/****************************************
                   SEH
 ****************************************/

static enum PCE_ERROR_CODE
PCE_SEH_start(
    PCE_context *pctx
)
{
    PCE_SEH_block* pseh = (PCE_SEH_block*) malloc(sizeof(PCE_SEH_block));
    if (!pseh) return PCE_MEMORY_ERROR;
    memset(pseh, 0, sizeof(PCE_SEH_block));
    pseh->pnext = pctx->pseh;
    pctx->pseh = pseh;
    return PCE_SUCCESS;
}

static void
PCE_SEH_free(
    PCE_SEH_block *pseh
)
{
    PCE_SEH_catchblock *pc1, *pc2;

    for (pc1 = pseh->pcatchers; pc1; ) {
	pc2 = pc1->pnext;
	free(pc1);
	pc1 = pc2;
    }
    free(pseh);
}

static enum PCE_ERROR_CODE
PCE_SEH_catch(
    PCE_context *pctx,
    enum PCE_ERROR_CODE e,
    PCE_SEH_CATCH_FUNCTION f,
    void *arg
)
{
    PCE_SEH_catchblock *pc;
    PCE_SEH_block *pseh = pctx->pseh;

    pc = (PCE_SEH_catchblock*) malloc(sizeof(PCE_SEH_catchblock));
    if (!pc) return PCE_MEMORY_ERROR;
    pc->pnext = pseh->pcatchers;
    pc->type = e;
    pc->pcfs = f;
    pc->catcharg = arg;
    pseh->pcatchers = pc;

    return PCE_SUCCESS;
}

static enum PCE_ERROR_CODE
PCE_SEH_try(
    PCE_context *pctx,
    PCE_SEH_TRY_FUNCTION tf,
    void *arg
)
{
    int ecode;
    enum PCE_ERROR_CODE r;
    PCE_SEH_block *pseh = pctx->pseh;

    if ((ecode = setjmp(pseh->jmp)) != 0) {
	PCE_SEH_catchblock *pcb;

	PCE_SEH_block *psehh1, *psehh2;
	for (psehh1 = pctx->pseh; psehh1 != pseh; ) {
	    psehh2 = psehh1->pnext;
	    PCE_SEH_free(psehh1);
	    psehh1 = psehh2;
	}
	for (pcb = pseh->pcatchers; pcb; pcb = pcb->pnext) {
	    if ((pcb->type == PCE_ANY_ERROR)
		|| (ecode == pcb->type)) {
		void *throwarg = pseh->arg;
		void *catcharg = pcb->catcharg;
		PCE_SEH_CATCH_FUNCTION pcfs = pcb->pcfs;
		pctx->pseh = pseh->pnext;
		PCE_SEH_free(pseh);
		r = (*pcfs)(pctx, ecode, throwarg, catcharg);
		return r;
	    }
	}

	/* fail to find out any handlers,
	   thus propagate the exception.  */
	if (!pseh->pnext) abort();
	pseh->pnext->arg = pseh->arg;
	pctx->pseh = pseh->pnext;
	PCE_SEH_free(pseh);
	longjmp(pctx->pseh->jmp, ecode);
    } else {
	r = (*tf)(pctx, arg);
    }

    pctx->pseh = pseh->pnext;
    PCE_SEH_free(pseh);

    return r;
}

static void
PCE_SEH_throw(
    PCE_context *pctx,
    int ecode,
    void *arg
)
{
    PCE_SEH_block *pseh = pctx->pseh;
    pseh->arg = arg;
    longjmp(pseh->jmp, ecode);
}

/****************************************
          Expression Evaluation
 ****************************************/

static EIMIL_value*
PCE_call(
    PCE_context *pctx,
    PCE_function *pf,
    PCE_code *parg
);

static EIMIL_value*
PCE_eval(
    PCE_context *pctx,
    PCE_code *pc
)
{
    EIMIL_value *pv;

    for (;;) {
	if (!pc) break;
	switch (pc->type) {
	  case PCE_CODE_VALUE:
	   pv = pc->val.pv;
	   pctx->pcur = pc->pnext;
	   return pv;
	  case PCE_CODE_SYMBOL:
	   pv = PCE_symbol_value(pctx, pc);
	   pctx->pcur = pc->pnext;
	   return pv;

	  case PCE_CODE_JMP:
	   pc = pctx->pcur = pc->val.pc_to;
	   continue;

	  case PCE_CODE_COND_JMP:
	   pv = PCE_eval(pctx, pc->parg);
	   if (pv) {
	       EIMIL_REFCHECK(*pv);
	       pc = pctx->pcur = pc->val.pc_to;
	   } else {
	       pc = pctx->pcur = pc->pnext;
	   }
	   continue;

	  case PCE_CODE_COND_NOT_JMP:
	   pv = PCE_eval(pctx, pc->parg);
	   if (pv) {
	       EIMIL_REFCHECK(*pv);
	       pc = pctx->pcur = pc->pnext;
	   } else {
	       pc = pctx->pcur = pc->val.pc_to;
	   }
	   continue;

	  case PCE_CODE_FUNCTION:
	   pv = PCE_call(pctx, pc->val.pf, pc->parg);
	   pctx->pcur = pc->pnext;
	   return pv;

	  default:
	   ASSERT(pc->type == PCE_CODE_INST);
	   pctx->pcur = pc;
	   pv = (*pc->val.h)(pctx);
	   pctx->pcur = pc->pnext;

	   return pv;
	}
    }

    return NULL;
}

enum PCE_ERROR_CODE
PCE_execute_loop(
    PCE_context *pctx,
    void *arg
)
{
    PCE_code *pc;
    EIMIL_value *pv;

    if (!pctx->pcur) {
	pc = pctx->ppce_data->pcode;
	ASSERT(pc->type == PCE_CODE_MAIN);
	pc = pc->parg;
	pctx->pcur = pc;
    }
    for (;;){
	pc = pctx->pcur;
	if (!pc) break;
	pv = PCE_eval(pctx, pc);
	if (pv) EIMIL_REFCHECK(*pv);
    }

    return PCE_SUCCESS;
}

static void
PCE_bind_function_arg(
    PCE_context *pctx,
    PCE_function *pf,
    PCE_code *parg
)
{
    int i;
    PCE_funcproto *pfp;
    EIMIL_value *pv;
    int idx = pctx->depth++;

    pctx->pfuncdic[idx] = pf->pdic;
    for (i = 0, pfp = pf->pfp; i < pf->nargs; i++, pfp++) {
	ASSERT(parg);
	pv = PCE_eval(pctx, parg);
	pfp->psym->obj.v.pv = pv;
	if (pv) EIMIL_ADDREF(*pv);
	parg = parg->pnext;
    }
    pctx->pdic_f = pf->pdic;

    return;
}

static void
PCE_unbind_function_arg(
    PCE_context *pctx,
    PCE_function *pf
)
{
    int i;
    PCE_funcproto *pfp;
    EIMIL_value *pv;
    int idx = --pctx->depth;

    if (idx == 0)
	pctx->pdic_f = NULL;
    else
	pctx->pdic_f = pctx->pfuncdic[idx - 1];

    for (i = 0, pfp = pf->pfp; i < pf->nargs; i++, pfp++) {
	pv = pfp->psym->obj.v.pv;
	if (pv) EIMIL_RMREF(*pv);
    }

    return;
}

struct PCE_call_catcharg {
    EIMIL_value *pv;
    PCE_function *pf;
};
static enum PCE_ERROR_CODE
PCE_call_exception_handler(
    PCE_context *pctx,
    int ecode,
    void *throwarg,
    void *catcharg
)
{
    struct PCE_call_catcharg *pcarg = (struct PCE_call_catcharg*) catcharg;

    if (ecode == PCE_RETURN_JMP_ERROR) {
	pcarg->pv = (EIMIL_value*) throwarg;
    } else {
	PCE_unbind_function_arg(pctx, pcarg->pf);
	PCE_SEH_throw(pctx, ecode, throwarg);
    }

    return PCE_SUCCESS;
}

static EIMIL_value*
PCE_call(
    PCE_context *pctx,
    PCE_function *pf,
    PCE_code *parg
)
{
    enum PCE_ERROR_CODE r;
    struct PCE_call_catcharg carg;

    if (pctx->depth >= PCE_CALL_MAX_DEPTH) {
	PCE_SEH_throw(pctx, PCE_OVER_EVAL_DEPTH_ERROR, NULL);
    }

    if (PCE_SEH_start(pctx) != PCE_SUCCESS) return NULL;

    PCE_bind_function_arg(pctx, pf, parg);
    carg.pv = NULL;
    carg.pf = pf;
    if (PCE_SEH_catch(pctx, PCE_ANY_ERROR,
		      PCE_call_exception_handler,
		      &carg)
	!= PCE_SUCCESS)
	return NULL;

    pctx->pcur = pf->pc;
    r = PCE_SEH_try(pctx, PCE_execute_loop, NULL);
    PCE_unbind_function_arg(pctx, pf);
    if (r == PCE_SUCCESS) return carg.pv;

    return NULL;
}

/*
  Retrieve the argument of current instruction.
  Return code:
      PCE_SUCCESS
            --> Successfully retrieve the value of argument.
      PCE_NO_MORE_ARG_ERROR
            --> There is no arg of idx.
  Exception:
      PCE_WRONG_TYPE_ARGUMENT_ERROR
 */
static enum PCE_ERROR_CODE
PCE_get_arg(
    PCE_context *pctx,
    int idx,
    int type,
    EIMIL_value** ppv
)
{
    int i;
    EIMIL_value *pv;
    PCE_code *pc_orig, *pc;

    pc_orig = pctx->pcur;
    pc = pc_orig->parg;
    for (i = 0; i < idx; i++) {
	if (!pc) break;
	pc = pc->pnext;
    }
    if (!pc) return PCE_NO_MORE_ARG_ERROR;
    pv = PCE_eval(pctx, pc);
    pctx->pcur = pc_orig;

    if ((type & EIMIL_TYPE_ANY)
	|| (!pv && (type & EIMIL_TYPE_NIL))
	|| (pv && ((pv->type & type) != 0))) {
	*ppv = pv;
	return PCE_SUCCESS;
    }

    PCE_SEH_throw(pctx, PCE_WRONG_TYPE_ARGUMENT_ERROR, NULL);
    return PCE_UNKNOWN_ERROR;
}

static EIMIL_symbol*
PCE_get_symbol_arg(
    PCE_context *pctx,
    int idx,
    enum EIMIL_CATEGORY cat
)
{
    int i;
    EIMIL_symbol *psym;
    PCE_code *pc = pctx->pcur;

    pc = pc->parg;
    for (i = 0; i < idx; i++) {
	if (!pc) {
	    ERROR_INTERNAL("!!Invalid PCE_code(Too few args).");
	}
	pc = pc->pnext;
    }
    ASSERT(pc->type == PCE_CODE_SYMBOL);
    psym = PCE_lookup_symbol(pctx, pc);
    ASSERT(psym);
    ASSERT(psym->cat == cat);

    return psym;
}

static enum PCE_ERROR_CODE
PCE_get_arg_or_error(
    PCE_context *pctx,
    int idx,
    int type,
    EIMIL_value** ppv
)
{
    enum PCE_ERROR_CODE r = PCE_get_arg(pctx, idx, type, ppv);
    if (r == PCE_NO_MORE_ARG_ERROR) {
	PCE_SEH_throw(pctx, PCE_TOO_FEW_ARGUMENTS_ERROR, NULL);
    } else if (r != PCE_SUCCESS) {
	PCE_SEH_throw(pctx, PCE_UNKNOWN_ERROR, NULL);
    }
    return r;
}

static int
PCE_get_arg_totnum(
    PCE_context *pctx
)
{
    int i;
    PCE_code *pc = pctx->pcur;

    for (i = 0, pc = pc->parg; pc; i++, pc = pc->pnext);
    return i;
}

/****************************************
          Predefined variables.
 ****************************************/

static void
PCE_set_current_event(
    PCE_context *pctx,
    EIMIL_value *pv_event
)
{
    EIMIL_symbol *psym = pctx->psym_cev;

    psym->obj.v.pv = pv_event;
    EIMIL_ADDREF(*pv_event);

    return;
}

/****************************************
          Execution methods.
 ****************************************/

DEFINE_EXEC_METHOD(undo)
{
    /* TODO */
    return NULL;
}

DEFINE_EXEC_METHOD(mark_undo)
{
    /* TODO */
    return NULL;
}

DEFINE_EXEC_METHOD(try)
{
    /* TODO */
    return NULL;
}

DEFINE_EXEC_METHOD(catch)
{
    /* TODO */
    return NULL;
}

DEFINE_EXEC_METHOD(throw)
{
    /* TODO */
    return NULL;
}

DEFINE_EXEC_METHOD(keycase)
{
    /* TODO */
    return NULL;
}

DEFINE_EXEC_METHOD(keymap)
{
    /* TODO */
    return NULL;
}

DEFINE_EXEC_METHOD(toggle_preedit)
{
    /* TODO */
    return NULL;
}

DEFINE_EXEC_METHOD(toggle_lookup_choice)
{
    /* TODO */
    return NULL;
}

DEFINE_EXEC_METHOD(keyeventp)
{
    EIMIL_value *pv;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_EVENT, &pv);
    if (strcmp(pv->v.event.type, "key") == 0)
	return EIMIL_construct_bool(1);

    return NULL;
}

DEFINE_EXEC_METHOD(or)
{
    int i;
    EIMIL_value *pv;

    for (i = 0; ; i++) {
	if (PCE_get_arg(pctx, i, EIMIL_TYPE_ANY, &pv)
	    == PCE_NO_MORE_ARG_ERROR)
	    break;
 	if (pv) return EIMIL_construct_bool(1);
    }
    return NULL;
}

DEFINE_EXEC_METHOD(and)
{
    int i;
    EIMIL_value *pv;

    for (i = 0; ; i++) {
	if (PCE_get_arg(pctx, i, EIMIL_TYPE_ANY, &pv)
	    == PCE_NO_MORE_ARG_ERROR)
	    break;
	if (!pv) return NULL;
    }
    return EIMIL_construct_bool(1);
}

DEFINE_EXEC_METHOD(not)
{
    EIMIL_value *pv;
    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_ANY, &pv);

    if (pv) {
	EIMIL_REFCHECK(*pv);
	return NULL;
    }

    return EIMIL_construct_bool(1);
}

DEFINE_EXEC_METHOD(gt)
{
    EIMIL_value *pv1, *pv2, *pvr;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_NUMBER, &pv1);
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_NUMBER, &pv2);

    if (pv1->v.number > pv2->v.number)
	pvr = EIMIL_construct_bool(1);
    else
	pvr = NULL;
    EIMIL_REFCHECK(*pv1);
    EIMIL_REFCHECK(*pv2);

    return pvr;
}

DEFINE_EXEC_METHOD(lt)
{
    EIMIL_value *pv1, *pv2, *pvr;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_NUMBER, &pv1);
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_NUMBER, &pv2);

    if (pv1->v.number < pv2->v.number)
	pvr = EIMIL_construct_bool(1);
    else
	pvr = NULL;
    EIMIL_REFCHECK(*pv1);
    EIMIL_REFCHECK(*pv2);

    return pvr;
}

DEFINE_EXEC_METHOD(le)
{
    EIMIL_value *pv1, *pv2, *pvr;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_NUMBER, &pv1);
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_NUMBER, &pv2);

    if (pv1->v.number <= pv2->v.number)
	pvr = EIMIL_construct_bool(1);
    else
	pvr = NULL;
    EIMIL_REFCHECK(*pv1);
    EIMIL_REFCHECK(*pv2);

    return pvr;
}

DEFINE_EXEC_METHOD(ge)
{
    EIMIL_value *pv1, *pv2, *pvr;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_NUMBER, &pv1);
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_NUMBER, &pv2);

    if (pv1->v.number >= pv2->v.number)
	pvr = EIMIL_construct_bool(1);
    else
	pvr = NULL;
    EIMIL_REFCHECK(*pv1);
    EIMIL_REFCHECK(*pv2);

    return pvr;
}

DEFINE_EXEC_METHOD(eql)
{
    EIMIL_value *pv1, *pv2, *pvr;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_NUMBER, &pv1);
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_NUMBER, &pv2);

    if (pv1->v.number == pv2->v.number)
	pvr = EIMIL_construct_bool(1);
    else
	pvr = NULL;
    EIMIL_REFCHECK(*pv1);
    EIMIL_REFCHECK(*pv2);

    return pvr;
}

DEFINE_EXEC_METHOD(propval)
{
    int idx;
    EIMIL_value *pv_prop, *pv_idx, *pvr;
    EIMIL_prop *pprop;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_PROP, &pv_prop);
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_NUMBER, &pv_idx);

    pprop = &pv_prop->v.prop;
    idx = pv_idx->v.number;
    EIMIL_REFCHECK(*pv_idx);
    if ((idx < 0) || (pprop->size <= idx)) {
	EIMIL_REFCHECK(*pv_prop);
	return NULL;
    }
    pvr = pprop->pvals[idx];

    EIMIL_REFCHECK_GUARD(*pv_prop, *pvr);

    return pvr;
}

DEFINE_EXEC_METHOD(propsize)
{
    EIMIL_value *pv_prop;
    EIMIL_prop *pprop;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_PROP, &pv_prop);

    pprop = &pv_prop->v.prop;

    return EIMIL_construct_number(pprop->size);
}

DEFINE_EXEC_METHOD(propmbeg)
{
    EIMIL_value *pv_prop;
    EIMIL_prop *pprop;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_PROP, &pv_prop);

    pprop = &pv_prop->v.prop;

    return EIMIL_construct_number(pprop->st);
}

DEFINE_EXEC_METHOD(propmend)
{
    EIMIL_value *pv_prop;
    EIMIL_prop *pprop;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_PROP, &pv_prop);

    pprop = &pv_prop->v.prop;

    return EIMIL_construct_number(pprop->end);
}

DEFINE_EXEC_METHOD(evval)
{
    EIMIL_value *pv, *pvr;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_EVENT, &pv);

    pvr = pv->v.event.pv_val;
    EIMIL_REFCHECK_GUARD(*pv, *pvr);

    return pvr;
}

DEFINE_EXEC_METHOD(evmod)
{
    EIMIL_value *pv, *pvr;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_EVENT, &pv);

    pvr = pv->v.event.pv_mod;
    EIMIL_REFCHECK_GUARD(*pv, *pvr);

    return pvr;
}

DEFINE_EXEC_METHOD(strlen)
{
    int len;
    EIMIL_value *pv_mtext;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_MTEXT, &pv_mtext);

    len = pv_mtext->v.mtext.len;
    EIMIL_REFCHECK(*pv_mtext);

    return EIMIL_construct_number(len);
}

DEFINE_EXEC_METHOD(strcmp)
{
    int r;
    EIMIL_value *pv_mtext1, *pv_mtext2;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_MTEXT, &pv_mtext1);
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_MTEXT, &pv_mtext2);

    r = EIMIL_UTF32_strcmp(pv_mtext1->v.mtext.ustr, pv_mtext2->v.mtext.ustr);

    EIMIL_REFCHECK(*pv_mtext1);
    EIMIL_REFCHECK(*pv_mtext2);

    return EIMIL_construct_number(r);
}

DEFINE_EXEC_METHOD(add)
{
    int i, n;
    EIMIL_value *pv;

    n = 0;
    for (i = 0; ; i++) {
	if (PCE_get_arg(pctx, i, EIMIL_TYPE_NUMBER, &pv)
	    == PCE_NO_MORE_ARG_ERROR)
	    break;
	n += pv->v.number;
	EIMIL_REFCHECK(*pv);
    }
    pv = EIMIL_construct_number(n);
    if (!pv) PCE_SEH_throw(pctx, PCE_MEMORY_ERROR, NULL);

    return pv;
}

DEFINE_EXEC_METHOD(sub)
{
    int i, n;
    EIMIL_value *pv;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_NUMBER, &pv);
    n = pv->v.number;
    EIMIL_REFCHECK(*pv);
    for (i = 1; ; i++) {
	if (PCE_get_arg(pctx, i, EIMIL_TYPE_NUMBER, &pv)
	    == PCE_NO_MORE_ARG_ERROR)
	    break;
	n -= pv->v.number;
	EIMIL_REFCHECK(*pv);
    }
    pv = EIMIL_construct_number(n);
    if (!pv) PCE_SEH_throw(pctx, PCE_MEMORY_ERROR, NULL);

    return pv;
}

DEFINE_EXEC_METHOD(mul)
{
    int i, n;
    EIMIL_value *pv;

    n = 1;
    for (i = 0; ; i++) {
	if (PCE_get_arg(pctx, i, EIMIL_TYPE_NUMBER, &pv)
	    == PCE_NO_MORE_ARG_ERROR)
	    break;
	n *= pv->v.number;
	EIMIL_REFCHECK(*pv);
    }
    pv = EIMIL_construct_number(n);
    if (!pv) PCE_SEH_throw(pctx, PCE_MEMORY_ERROR, NULL);

    return pv;
}

DEFINE_EXEC_METHOD(div)
{
    int i, n;
    EIMIL_value *pv;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_NUMBER, &pv);
    n = pv->v.number;
    EIMIL_REFCHECK(*pv);
    for (i = 1; ; i++) {
	if (PCE_get_arg(pctx, i, EIMIL_TYPE_NUMBER, &pv)
	    == PCE_NO_MORE_ARG_ERROR)
	    break;
	n /= pv->v.number;
	EIMIL_REFCHECK(*pv);
    }
    pv = EIMIL_construct_number(n);
    if (!pv) PCE_SEH_throw(pctx, PCE_MEMORY_ERROR, NULL);

    return pv;
}

DEFINE_EXEC_METHOD(mod)
{
    int i, n;
    EIMIL_value *pv;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_NUMBER, &pv);
    n = pv->v.number;
    EIMIL_REFCHECK(*pv);
    for (i = 1; ; i++) {
	if (PCE_get_arg(pctx, i, EIMIL_TYPE_NUMBER, &pv)
	    == PCE_NO_MORE_ARG_ERROR)
	    break;
	n %= pv->v.number;
	EIMIL_REFCHECK(*pv);
    }
    pv = EIMIL_construct_number(n);
    if (!pv) PCE_SEH_throw(pctx, PCE_MEMORY_ERROR, NULL);

    return pv;
}

DEFINE_EXEC_METHOD(bor)
{
    int i, n;
    EIMIL_value *pv;

    if (PCE_get_arg(pctx, 0, EIMIL_TYPE_NUMBER, &pv)
	== PCE_NO_MORE_ARG_ERROR) {
	PCE_SEH_throw(pctx, PCE_TOO_FEW_ARGUMENTS_ERROR, NULL);
    }
    n = pv->v.number;
    EIMIL_REFCHECK(*pv);
    for (i = 1; ; i++) {
	if (PCE_get_arg(pctx, i, EIMIL_TYPE_NUMBER, &pv)
	    == PCE_NO_MORE_ARG_ERROR)
	    break;
	n |= pv->v.number;
	EIMIL_REFCHECK(*pv);
    }
    pv = EIMIL_construct_number(n);
    if (!pv) PCE_SEH_throw(pctx, PCE_MEMORY_ERROR, NULL);

    return pv;
}

DEFINE_EXEC_METHOD(band)
{
    int i, n;
    EIMIL_value *pv;

    if (PCE_get_arg(pctx, 0, EIMIL_TYPE_NUMBER, &pv)
	== PCE_NO_MORE_ARG_ERROR) {
	PCE_SEH_throw(pctx, PCE_TOO_FEW_ARGUMENTS_ERROR, NULL);
    }
    n = pv->v.number;
    EIMIL_REFCHECK(*pv);
    for (i = 1; ; i++) {
	if (PCE_get_arg(pctx, i, EIMIL_TYPE_NUMBER, &pv)
	    == PCE_NO_MORE_ARG_ERROR)
	    break;
	n &= pv->v.number;
	EIMIL_REFCHECK(*pv);
    }
    pv = EIMIL_construct_number(n);
    if (!pv) PCE_SEH_throw(pctx, PCE_MEMORY_ERROR, NULL);

    return pv;
}

DEFINE_EXEC_METHOD(bxor)
{
    int i, n;
    EIMIL_value *pv;

    if (PCE_get_arg(pctx, 0, EIMIL_TYPE_NUMBER, &pv)
	== PCE_NO_MORE_ARG_ERROR) {
	PCE_SEH_throw(pctx, PCE_TOO_FEW_ARGUMENTS_ERROR, NULL);
    }
    n = pv->v.number;
    EIMIL_REFCHECK(*pv);
    for (i = 1; ; i++) {
	if (PCE_get_arg(pctx, i, EIMIL_TYPE_NUMBER, &pv)
	    == PCE_NO_MORE_ARG_ERROR)
	    break;
	n ^= pv->v.number;
	EIMIL_REFCHECK(*pv);
    }
    pv = EIMIL_construct_number(n);
    if (!pv) PCE_SEH_throw(pctx, PCE_MEMORY_ERROR, NULL);

    return pv;
}

DEFINE_EXEC_METHOD(UCSval)
{
    UTF32 ch;
    EIMIL_value *pv;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_CHAR, &pv);
    ch = pv->v.ch;
    EIMIL_REFCHECK(*pv);

    return EIMIL_construct_number((int)ch);
}

DEFINE_EXEC_METHOD(propadd)
{
    EIMIL_value *pv_prop, *pv_val;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_PROP, &pv_prop);
    PCE_get_arg_or_error(pctx, 1, pv_prop->v.prop.type, &pv_val);

    if (!EIMIL_add_prop(&pv_prop->v.prop, pv_val)) {
	PCE_SEH_throw(pctx, PCE_MEMORY_ERROR, NULL);
    }
    EIMIL_ADDREF(*pv_val);

    return pv_prop;
}

DEFINE_EXEC_METHOD(propcopy)
{
    EIMIL_value *pv_prop, *pv_prop2;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_PROP, &pv_prop);
    pv_prop2 = EIMIL_copy_value(pv_prop);

    if (!pv_prop2) {
	PCE_SEH_throw(pctx, PCE_MEMORY_ERROR, NULL);
    }

    return pv_prop2;
}

DEFINE_EXEC_METHOD(evchar)
{
    EIMIL_value *pv;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_EVENT, &pv);

    return pv->v.event.pv_char;
}

DEFINE_EXEC_METHOD(strref)
{
    int idx, len;
    UTF32* pstr, ch;
    EIMIL_value *pv_mtext, *pv_idx;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_MTEXT, &pv_mtext);
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_NUMBER, &pv_idx);

    pstr = pv_mtext->v.mtext.ustr;
    len = pv_mtext->v.mtext.len;
    idx = pv_idx->v.number;
    if ((idx < 0) || (idx >= len)) {
	PCE_SEH_throw(pctx, PCE_OUT_OF_RANGE_ERROR, NULL);
    }

    ch = pstr[idx];

    EIMIL_REFCHECK(*pv_mtext);
    EIMIL_REFCHECK(*pv_idx);

    return EIMIL_construct_char(ch);
}

DEFINE_EXEC_METHOD(makechar)
{
    int n;
    EIMIL_value *pv;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_NUMBER, &pv);
    n = pv->v.number;
    EIMIL_REFCHECK(*pv);

    return EIMIL_construct_char((UTF32)n);
}

DEFINE_EXEC_METHOD(evtype)
{
    EIMIL_value *pv;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_EVENT, &pv);

    return EIMIL_construct_mtext_from_UTF8(pv->v.event.type);
}

DEFINE_EXEC_METHOD(evmtext)
{
    EIMIL_value *pv, *pvr;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_EVENT, &pv);

    pvr = pv->v.event.pv_mtext;
    EIMIL_REFCHECK_GUARD(*pv, *pvr);

    return pvr;
}

DEFINE_EXEC_METHOD(concat)
{
    int i, n;
    EIMIL_value *pvr;
    EIMIL_value **ppv;

    n = PCE_get_arg_totnum(pctx);
    if (n == 0) return EIMIL_construct_mtext_from_UTF8("");

#if 1
    ppv = (EIMIL_value**) alloca(sizeof(EIMIL_value*) * n);
#else
    ppv = (EIMIL_value**) malloc(sizeof(EIMIL_value*) * n);
#endif

    for (i = 0; i < n; i++) {
	PCE_get_arg_or_error(pctx, i,
			     EIMIL_TYPE_CHAR | EIMIL_TYPE_MTEXT,
			     ppv + i);
    }

    pvr = EIMIL_mtext_concat(n, ppv);

    for (i = 0; i < n; i++) {
	EIMIL_REFCHECK(*(ppv[i]));
    }

#if 1
#else
    free(ppv);
#endif

    return pvr;
}

DEFINE_EXEC_METHOD(substr)
{
    int beg, end, len;
    EIMIL_value *pv_mtext, *pv_beg, *pv_end, *pvr;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_MTEXT, &pv_mtext);
    len = pv_mtext->v.mtext.len;
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_NUMBER, &pv_beg);
    beg = pv_beg->v.number;
    EIMIL_REFCHECK(*pv_beg);

    if (PCE_get_arg(pctx, 2, EIMIL_TYPE_NUMBER, &pv_end)
	== PCE_NO_MORE_ARG_ERROR) {
	end = len;
    } else {
	end = pv_end->v.number;
	EIMIL_REFCHECK(*pv_end);
    }

    if ((beg < 0) || (beg >= end) || (end > len)) {
	PCE_SEH_throw(pctx, PCE_OUT_OF_RANGE_ERROR, NULL);
    }

    pvr = EIMIL_mtext_substr(pv_mtext, beg, end);

    EIMIL_REFCHECK(*pv_mtext);

    return pvr;
}

DEFINE_EXEC_METHOD(next)
{
    EIMIL_value *pv_event;

    if (PCE_get_arg(pctx, 0, EIMIL_TYPE_ANY, &pv_event)
	== PCE_SUCCESS) {
	EIMIL_reply_event(pctx->ped, pv_event);
    }
    EIMIL_REFCHECK(*pv_event);

    pv_event = EIMIL_next_event(pctx->ped);

    if (pv_event) {
	PCE_set_current_event(pctx, pv_event);
    } else {
	PCE_SEH_throw(pctx, PCE_WAIT_NEXT_EVENT_ERROR, NULL);
    }

    return NULL;
}

DEFINE_EXEC_METHOD(makeev)
{
    UTF8* type;
    EIMIL_value *pv_type, *pv_val, *pv_mod, *pv_char, *pv_mtext;
    EIMIL_value *ret;

    pv_val = pv_char = pv_mtext = NULL;
    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_MTEXT, &pv_type);
    
    type = EIMIL_convert_UTF32_to_UTF8(pv_type->v.mtext.ustr);
    if (!type) {
	PCE_SEH_throw(pctx, PCE_MEMORY_ERROR, NULL);
    }
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_NUMBER | EIMIL_TYPE_NIL, &pv_val);
    PCE_get_arg_or_error(pctx, 2, EIMIL_TYPE_NUMBER | EIMIL_TYPE_NIL, &pv_mod);
    PCE_get_arg_or_error(pctx, 3, EIMIL_TYPE_CHAR | EIMIL_TYPE_NIL, &pv_char);
    PCE_get_arg_or_error(pctx, 4, EIMIL_TYPE_MTEXT | EIMIL_TYPE_NIL, &pv_mtext);

    ret = EIMIL_construct_event(type, pv_val, pv_mod, pv_char, pv_mtext);

    if (type) free(type);
    
    return ret;
}

DEFINE_EXEC_METHOD(commit)
{
    return NULL;
}

DEFINE_EXEC_METHOD(unroll)
{
    return NULL;
}

DEFINE_EXEC_METHOD(propdel)
{
    int idx;
    EIMIL_value *pv_prop, *pv_idx;
    EIMIL_prop *pprop;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_PROP, &pv_prop);
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_NUMBER, &pv_idx);

    pprop = &pv_prop->v.prop;
    idx = pv_idx->v.number;
    EIMIL_delete_prop(pprop, idx);
    EIMIL_REFCHECK(*pv_prop);
    EIMIL_REFCHECK(*pv_idx);

    return NULL;
}

DEFINE_EXEC_METHOD(addmprop)
{
    int beg, end, len;
    EIMIL_value *pv_target, *pv_prop, *pv_beg, *pv_end;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_MTEXT, &pv_target);
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_PROP, &pv_prop);
    PCE_get_arg_or_error(pctx, 2, EIMIL_TYPE_NUMBER, &pv_beg);
    PCE_get_arg_or_error(pctx, 3, EIMIL_TYPE_NUMBER, &pv_end);

    len = pv_target->v.mtext.len;
    beg = pv_beg->v.number;
    end = pv_end->v.number;

    if ((beg < 0) || (beg >= end) || (end > len)) {
	PCE_SEH_throw(pctx, PCE_OUT_OF_RANGE_ERROR, NULL);
    }

    EIMIL_add_prop_on_mtext(&pv_target->v.mtext, pv_prop, beg, end);

    EIMIL_REFCHECK(*pv_target);
    EIMIL_REFCHECK(*pv_prop);
    EIMIL_REFCHECK(*pv_beg);
    EIMIL_REFCHECK(*pv_end);

    return NULL;
}

DEFINE_EXEC_METHOD(delmprop)
{
    EIMIL_value *pv;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_PROP, &pv);

    EIMIL_detach_prop_from_mtext(pv);

    EIMIL_REFCHECK(*pv);

    return NULL;
}

DEFINE_EXEC_METHOD(setmprop)
{
    int beg, end, len;
    EIMIL_value *pv_target, *pv_prop, *pv_beg, *pv_end;

    PCE_get_arg_or_error(pctx, 0, EIMIL_TYPE_MTEXT, &pv_target);
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_PROP, &pv_prop);
    PCE_get_arg_or_error(pctx, 2, EIMIL_TYPE_NUMBER, &pv_beg);
    PCE_get_arg_or_error(pctx, 3, EIMIL_TYPE_NUMBER, &pv_end);

    len = pv_target->v.mtext.len;
    beg = pv_beg->v.number;
    end = pv_end->v.number;

    if ((beg < 0) || (beg >= end) || (end > len)) {
	PCE_SEH_throw(pctx, PCE_OUT_OF_RANGE_ERROR, NULL);
    }

    EIMIL_set_prop_on_mtext(&pv_target->v.mtext, pv_prop, beg, end);

    EIMIL_REFCHECK(*pv_target);
    EIMIL_REFCHECK(*pv_prop);
    EIMIL_REFCHECK(*pv_beg);
    EIMIL_REFCHECK(*pv_end);

    return NULL;
}

DEFINE_EXEC_METHOD(tblref)
{
    return NULL;
}

DEFINE_EXEC_METHOD(makeprop)
{
    EIMIL_symbol *psym;

    psym = PCE_get_symbol_arg(pctx, 0, EIMIL_CAT_PROPERTY);

    return EIMIL_construct_prop(psym);
}

DEFINE_EXEC_METHOD(getmprop)
{
    EIMIL_symbol *psym;
    EIMIL_value *pv_mtext, *pv_beg, *pvr;

    psym = PCE_get_symbol_arg(pctx, 0, EIMIL_CAT_PROPERTY);
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_MTEXT, &pv_mtext);
    PCE_get_arg_or_error(pctx, 2, EIMIL_TYPE_NUMBER, &pv_beg);

    pvr = EIMIL_get_prop_from_mtext(&pv_mtext->v.mtext,
				    psym, pv_beg->v.number);

    EIMIL_REFCHECK(*pv_mtext);
    EIMIL_REFCHECK(*pv_beg);

    return pvr;
}

DEFINE_EXEC_METHOD(findmprop)
{
    EIMIL_symbol *psym;
    EIMIL_value *pv_mtext, *pv_beg, *pvr;

    psym = PCE_get_symbol_arg(pctx, 0, EIMIL_CAT_PROPERTY);
    PCE_get_arg_or_error(pctx, 1, EIMIL_TYPE_MTEXT, &pv_mtext);
    PCE_get_arg_or_error(pctx, 2, EIMIL_TYPE_NUMBER, &pv_beg);

    pvr = EIMIL_find_prop_from_mtext(&pv_mtext->v.mtext,
				     psym, pv_beg->v.number);

    EIMIL_REFCHECK(*pv_mtext);
    EIMIL_REFCHECK(*pv_beg);

    return pvr;
}

DEFINE_EXEC_METHOD(interact)
{
    return NULL;
}

DEFINE_EXEC_METHOD(match)
{
    return NULL;
}

DEFINE_EXEC_METHOD(forward)
{
    return NULL;
}

DEFINE_EXEC_METHOD(send)
{
    return NULL;
}

DEFINE_EXEC_METHOD(set)
{
    EIMIL_value *pv, *pvo;
    EIMIL_symbol *psym;

    ASSERT(pctx->pcur->parg && pctx->pcur->parg->pnext);
    psym = PCE_lookup_symbol(pctx, pctx->pcur->parg->pnext);
    ASSERT(psym && psym->cat == EIMIL_CAT_VARIABLE);

    if (!PCE_get_arg(pctx, 0, EIMIL_TYPE_ANY, &pv)
	== PCE_NO_MORE_ARG_ERROR) {
	PCE_SEH_throw(pctx, PCE_TOO_FEW_ARGUMENTS_ERROR, NULL);
    }
    if (psym->obj.v.constp) {
	PCE_SEH_throw(pctx, PCE_VARIABLE_CONSTANT_ERROR, NULL);
    }
    if (pv && (pv->type != psym->obj.v.type)) {
	PCE_SEH_throw(pctx, PCE_WRONG_TYPE_ARGUMENT_ERROR, NULL);
    }
    pvo = psym->obj.v.pv;
    if (pvo) EIMIL_RMREF(*pvo);
    psym->obj.v.pv = pv;
    if (pv) EIMIL_ADDREF(*pv);

    return pv;
}

DEFINE_EXEC_METHOD(return)
{
    EIMIL_value *pv;

    if (!PCE_get_arg(pctx, 0, EIMIL_TYPE_ANY, &pv)
	== PCE_NO_MORE_ARG_ERROR) {
	PCE_SEH_throw(pctx, PCE_TOO_FEW_ARGUMENTS_ERROR, NULL);
    }
    PCE_SEH_throw(pctx, PCE_RETURN_JMP_ERROR, pv);
    return NULL;
}

static enum PCE_ERROR_CODE
PCE_root_exception_handler(
    PCE_context *pctx,
    int ecode,
    void *throwarg,
    void *catcharg
)
{
    /* TODO */
    if (ecode == PCE_WAIT_NEXT_EVENT_ERROR) {
	pctx->pcur = pctx->pcur->pnext;
	return PCE_WAIT_NEXT_EVENT_ERROR;
    }

    EIMIL_set_error(pctx->ped, "Exception received.");
    return PCE_UNKNOWN_ERROR;
}

static enum EIMIL_ENGINE_STATUS
PCE_execute(
    void *private
)
{
    EIMIL_value *pv;
    PCE_context *pctx = (PCE_context*) private;
    enum PCE_ERROR_CODE r;

    pv = EIMIL_next_event(pctx->ped);
    if (pv) {
	PCE_set_current_event(pctx, pv);
    }

    if (PCE_SEH_start(pctx) != PCE_SUCCESS)
	return EIMIL_ENGINE_STATUS_ERROR;
    if (PCE_SEH_catch(pctx, PCE_ANY_ERROR,
		      PCE_root_exception_handler,
		      NULL)
	!= PCE_SUCCESS)
	return EIMIL_ENGINE_STATUS_ERROR;
    r = PCE_SEH_try(pctx, PCE_execute_loop, NULL);

    if (r == PCE_SUCCESS)
	return EIMIL_ENGINE_STATUS_SUCCESS;
    else if (r == PCE_WAIT_NEXT_EVENT_ERROR)
	return EIMIL_ENGINE_STATUS_SUSPENDED;

    return EIMIL_ENGINE_STATUS_ERROR;
}

/********************************************************************************
			       initialization
*********************************************************************************/

static void
PCE_init_document_template()
{
    int n;
    EIMIL_element_template *p;

    p = PCE_if_full_template;
    n = EIMIL_TEMPLATE_NUM(PCE_if_else_template) - 1;
    memcpy(p , PCE_if_else_template, n * sizeof(*p));
    p += n;
    n = EIMIL_TEMPLATE_NUM(PCE_statement_template);
    memcpy(p , PCE_statement_template, n * sizeof(*p));

    p = PCE_try_full_template;
    n = EIMIL_TEMPLATE_NUM(PCE_try_catch_template) - 1;
    memcpy(p , PCE_try_catch_template, n * sizeof(*p));
    p += n;
    n = EIMIL_TEMPLATE_NUM(PCE_statement_template);
    memcpy(p , PCE_statement_template, n * sizeof(*p));
}

int
PCE_init()
{
    PCE_init_document_template();
    EIMIL_register_engine(PCE_classname,
			  PCE_docroot,
			  PCE_handler,
			  PCE_execute,
			  PCE_xmlns_uri);
    return 1;
}

/********************************************************************************
			            API
*********************************************************************************/

#ifdef DEBUG
static void
dump_header(
    int lv,
    PCE_code *pc
)
{
    int i;
    for (i = 0; i < lv; i++) {
	fputs("  ", stderr);
    }
    fprintf(stderr, "%X:", (int) pc);
}

void
EIMIL_dump_value(
    EIMIL_value *pv
)
{
    if (pv) {
	switch (pv->type) {
	  case EIMIL_TYPE_BOOL:
	   fprintf(stderr, "Bool:%d", pv->v.bool_val);
	   break;
	  case EIMIL_TYPE_NUMBER:
	   fprintf(stderr, "Int:%d", pv->v.number);
	   break;
	  case EIMIL_TYPE_CHAR:
	   fprintf(stderr, "Char:%c(%X)", pv->v.ch, pv->v.ch);
	   break;
	  case EIMIL_TYPE_MTEXT:
	   fprintf(stderr, "MTEXT");
	   break;
	  case EIMIL_TYPE_EVENT:
	   fprintf(stderr, "EVENT");
	   break;
	  case EIMIL_TYPE_PROP:
	   fprintf(stderr, "PROP");
	   break;
	  default:
	   fprintf(stderr, "!!Unknown value:%X", pv->type);
	   break;
	}
    } else {
	fprintf(stderr, "nil");
    }
    fputc('\n', stderr);
}

void
PCE_dump_code_internal(
    PCE_context *pctx,
    PCE_code *pc,
    int lv
)
{
    char *pn;
    EIMIL_symbol *psym;

    for ( ; pc; pc = pc->pnext) {
	switch (pc->type) {
	  case PCE_CODE_MAIN:
	   dump_header(lv, pc);
	   fprintf(stderr, "MAIN:\n");
	   PCE_dump_code_internal(pctx, pc->parg, lv + 1);
	   break;
	  case PCE_CODE_DEFUN:
	   dump_header(lv, pc);
	   psym = PCE_lookup_symbol(pctx, pc);
	   fprintf(stderr, "DEFUN:%s\n", psym->name);
	   PCE_dump_code_internal(pctx, pc->parg, lv + 1);
	   break;
	  case PCE_CODE_INST:
	   if (pc->val.h == PCE_add_exec) {
	      pn = "add";
	   } else if (pc->val.h == PCE_sub_exec) {
	      pn = "sub";
	   } else if (pc->val.h == PCE_mul_exec) {
	      pn = "mul";
	   } else if (pc->val.h == PCE_div_exec) {
	      pn = "div";
	   } else if (pc->val.h == PCE_lt_exec) {
	      pn = "lt";
	   } else if (pc->val.h == PCE_gt_exec) {
	      pn = "gt";
	   } else if (pc->val.h == PCE_le_exec) {
	      pn = "le";
	   } else if (pc->val.h == PCE_ge_exec) {
	      pn = "ge";
	   } else if (pc->val.h == PCE_eql_exec) {
	      pn = "eql";
	   } else if (pc->val.h == PCE_strcmp_exec) {
	      pn = "strcmp";
	   } else if (pc->val.h == PCE_set_exec) {
	      pn = "set";
	   } else if (pc->val.h == PCE_next_exec) {
	      pn = "next";
	   } else if (pc->val.h == PCE_makeev_exec) {
	      pn = "makeev";
	   } else {
	      pn = "unknown";
	   }
	   dump_header(lv, pc);
	   fprintf(stderr, "Inst:%s\n", pn);
	   PCE_dump_code_internal(pctx, pc->parg, lv + 1);
	   break;
	  case PCE_CODE_VALUE:
	   dump_header(lv, pc);
	   EIMIL_dump_value(pc->val.pv);
	   break;
	  case PCE_CODE_SYMBOL:
	   dump_header(lv, pc);
	   psym = PCE_lookup_symbol(pctx, pc);
	   if (!psym) {
	       fprintf(stderr,
		       "!!Unknown symid:%d\n",
		       pc->val.symid);
	       break;
	   }
	   if (psym->cat != EIMIL_CAT_VARIABLE) {
	       fprintf(stderr,
		       "!!not variable:%s\n",
		       psym->name);
	       break;
	   }
	   fprintf(stderr, "Var:%s\n", psym->name);
	   break;
	  case PCE_CODE_JMP:
	   dump_header(lv, pc);
	   fprintf(stderr, "Jump:%x\n", (int) pc->val.pc_to);
	   PCE_dump_code_internal(pctx, pc->parg, lv + 1);
	   break;
	  case PCE_CODE_COND_JMP:
	   dump_header(lv, pc);
	   fprintf(stderr, "Jump if:%x\n", (int) pc->val.pc_to);
	   PCE_dump_code_internal(pctx, pc->parg, lv + 1);
	   break;
	  case PCE_CODE_COND_NOT_JMP:
	   dump_header(lv, pc);
	   fprintf(stderr, "Jump unless:%x\n", (int) pc->val.pc_to);
	   PCE_dump_code_internal(pctx, pc->parg, lv + 1);
	   break;
          default:
           /* TODO */
           break;
	}
    }
}

void
PCE_dump_code(
    PCE_context *pctx
)
{
    PCE_dump_code_internal(pctx, pctx->pcur, 0);
}

#endif

/* Local Variables: */
/* c-file-style: "iiim-project" */
/* End: */
