/****************************************************************************
 * KONOHA COPYRIGHT, LICENSE NOTICE, AND DISCRIMER
 *
 * Copyright (c) 2005-2008, Kimio Kuramitsu <kimio at ynu.ac.jp>
 *           (c) 2008-      Konoha Software Foundation
 * All rights reserved.
 *
 * You may choose one of the following two licenses when you use konoha.
 * See www.konohaware.org/license.html for further information.
 *
 * (1) GNU General Public License 2.0      (with    KONOHA_UNDER_GPL2)
 * (2) Konoha Software Foundation License 1.0
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/* ************************************************************************ */

#include"commons.h"

/* ************************************************************************ */

#ifdef __cplusplus
extern "C" {
#endif

/* ======================================================================== */
/* [EXPR] */

static void KNH_ASM_PPUSH(Ctx *ctx, Compiler *cpr, Term *tm, int level);
void knh_Stmt_terms_typing(Ctx *ctx, Stmt *stmt, size_t n, Compiler *cpr, NameSpace *ns, knh_class_t reqc);

/* ======================================================================== */
/* [typing] */

void knh_Stmt_setType(Stmt *stmt, knh_type_t type)
{
	knh_Stmt_setTyped(stmt, 1);
	DP(stmt)->type = type;
}

/* ------------------------------------------------------------------------ */

knh_bool_t knh_Term_isTyped(Term *tm)
{
	if(IS_Token(tm)) {
		return knh_Token_isTyped((Token*)tm);
	}
	else {
		return knh_Stmt_isTyped((Stmt*)tm);
	}
}

/* ------------------------------------------------------------------------ */

knh_type_t knh_Term_get_type(Term *tm)
{
	if(IS_Token(tm)) {
		Token *tk = (Token*)tm;
		DEBUG3_ASSERT(knh_Token_isTyped(tk));
		return DP(tk)->type;
	}
	else {
		Stmt *stmt = (Stmt*)tm;
		DEBUG3_ASSERT(IS_Stmt(stmt));
		DEBUG3_ASSERT(knh_Stmt_isTyped(stmt));
		return DP(stmt)->type;
	}
}

/* ------------------------------------------------------------------------ */

knh_type_t knh_Term_get_cid(Term *tm)
{
	if(IS_Token(tm)) {
		Token *tk = (Token*)tm;
		DEBUG3_ASSERT(knh_Token_isTyped(tk));
		return TYPE_UNMASK_NN(DP(tk)->type);
	}
	else {
		Stmt *stmt = (Stmt*)tm;
		DEBUG3_ASSERT(IS_Stmt(stmt));
		DEBUG3_ASSERT(knh_Stmt_isTyped(stmt));
		return TYPE_UNMASK_NN(DP(stmt)->type);
	}
}

/* ------------------------------------------------------------------------ */

static
knh_type_t knh_Term_get_bcid(Term *tm)
{
	knh_class_t cid;
	if(IS_Token(tm)) {
		Token *tk = (Token*)tm;
		DEBUG3_ASSERT(knh_Token_isTyped(tk));
		cid = TYPE_UNMASK_NN(DP(tk)->type);
	}
	else {
		Stmt *stmt = (Stmt*)tm;
		DEBUG3_ASSERT(IS_Stmt(stmt));
		DEBUG3_ASSERT(knh_Stmt_isTyped(stmt));
		cid = TYPE_UNMASK_NN(DP(stmt)->type);
	}
	DEBUG_ASSERT_cid(cid);
	return knh_tClass[cid].bcid;
}

/* ======================================================================== */
/* [typing] */

static
knh_bool_t knh_StmtCALL_isCONST(Stmt *stmt, Method *mtd)
{
	if(knh_Method_isStatic(mtd)) {
		int i;
		for(i = 1; i < DP(stmt)->size; i++) {
			Token *tk = DP(stmt)->tokens[i];
			if(!IS_Token(tk)) return 0;
			if(SP(tk)->tt != TT_CONST) return 0;
		}
		return 1;
	}
	return 0;
}

/* ------------------------------------------------------------------------ */

static
Term *knh_StmtCALL_toCONST(Ctx *ctx, Stmt *stmt, Method *mtd)
{
	int i;
	DEBUG3_ASSERT(IS_Method(mtd));
	DEBUG3("STMT TO CONST ..");
	{
		KNH_LPUSH(ctx, mtd);    /* ebp[-3] */
		for(i = 1; i < DP(stmt)->size; i++) {
			Token *tk = DP(stmt)->tokens[i];
			KNH_LPUSH(ctx, DP(tk)->data);
		}
		KNH_SCALL(ctx, (DP(stmt)->size - 1));
		VM_SHIFT(ctx, -1);
//		if(IS_String(ctx->ebp[1].o)) {
//			DEBUG3("result='%s'", knh_String_tochar((String*)ctx->ebp[1].o));
//		}
		return UP(new_TokenCONST(ctx, UP(stmt), ctx->ebp[1].o));
	}
}

/* ======================================================================== */
/* [typing] */

static
void knh_Token_setMETHOD(Ctx *ctx, Token *o, knh_methodn_t mn, Method *mtd, knh_type_t rtype)
{
	if(knh_token_tomethodn(SP(o)->tt) != METHODN_NONAME) {
		KNH_ASSERT(IS_NOTNULL(mtd));
		DP(o)->tt_op = SP(o)->tt;
	}
	else {
		DP(o)->mn = mn;
	}
	SP(o)->tt = TT_MTDMPR;
	KNH_SETv(ctx, DP(o)->data, mtd);
	DP(o)->type = rtype;
}

/* ------------------------------------------------------------------------ */

static
Term *knh_Stmt_foundTypeError(Ctx *ctx, Stmt *stmt, Compiler *cpr)
{
//	DEBUG3("switching %s to STT_ERR", knh_stmt_tochar(SP(stmt)->stt));
	knh_Stmt_toERR(ctx, stmt, TS_TYPEERR);
	return UP(stmt);
}

/* ------------------------------------------------------------------------ */

static
Term *knh_StmtPARAMS_typing(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t btype, Method *mtd)
{
	KNH_ASSERT(IS_Method(mtd));

	int i, size = DP(stmt)->size;
	knh_class_t self_cid = TYPE_UNMASK_NN(btype);
	knh_type_t rtype = knh_pmztype_totype(ctx, knh_Method_rtype(mtd), self_cid);
	if(TYPE_ISNULLABLE(btype) && !knh_Method_isNullBase(mtd)) {
		//DEBUG3("Null OBJECT Pattern!!");
		rtype = TYPE_UNMASK_NN(rtype);
	}

	for(i = 0; i < knh_Method_psize(mtd); i++) {
		knh_type_t reqt = knh_pmztype_totype(ctx, knh_Method_ptype(mtd, i), self_cid);
		int n = i + 2;
		//DEBUG3("reqt[%d]=%d,%s%s", n, reqt, TYPEQN(reqt));
		if(n < size) {
			knh_Stmt_terms_typing(ctx, stmt, n, cpr, ns, TYPE_UNMASK_NN(reqt));
		}
		else {
			if(TYPE_ISNOTNULL(reqt)) {
				char bufm[CLASSNAME_BUFSIZ];
				knh_format_methodn(bufm, sizeof(bufm), DP(mtd)->mn);
				knh_Compiler_perror(ctx, cpr, KMSG_ETOOFEWPARAMS, bufm);
				return knh_Stmt_foundTypeError(ctx, stmt, cpr);
			}
			else if(!knh_Method_isVarArgs(mtd)) {
				//DEBUG3("add null");
				knh_Stmt_add(ctx, stmt, UP(new_TokenCONST(ctx, UP(stmt), KNH_NULL)));
			}
		}
	}
	if(knh_Method_isVarArgs(mtd)) {
		knh_type_t reqt = knh_pmztype_totype(ctx, knh_Method_ptype(mtd, knh_Method_psize(mtd) - 1), self_cid);
		for(i = knh_Method_psize(mtd); i+2 < size; i++) {
			//DEBUG3("reqt[%d]=%d,%s%s", i+2, reqt, TYPEQN(reqt));
			knh_Stmt_terms_typing(ctx, stmt, i+2, cpr, ns, TYPE_UNMASK_NN(reqt));
		}
	}
	else if(i + 2 != DP(stmt)->size) {
		char bufm[CLASSNAME_BUFSIZ];
		knh_format_methodn(bufm, sizeof(bufm), DP(mtd)->mn);
		knh_Compiler_perror(ctx, cpr, KMSG_WTOOMANYPARAMS, bufm);
		DP(stmt)->size = i + 2;
	}

	if(knh_StmtCALL_isCONST(stmt, mtd)) {
		return knh_StmtCALL_toCONST(ctx, stmt, mtd);
	}
	knh_Token_setMETHOD(ctx, DP(stmt)->tokens[0], DP(mtd)->mn, mtd, rtype);
	knh_Stmt_setType(stmt, rtype);
	return UP(stmt);
}

/* ------------------------------------------------------------------------ */
/* [CALL1] */

Term *knh_StmtCALL1_typing(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_class_t reqc)
{
	DEBUG3_ASSERT(DP(stmt)->size == 1);
	knh_Stmt_terms_typing(ctx, stmt, 0, cpr, ns, reqc);
	return DP(stmt)->terms[0];
}

/* ------------------------------------------------------------------------ */

knh_methodn_t knh_Token_tomethodn(Ctx *ctx, Token *o)
{
	DEBUG3_ASSERT(IS_Token(o));
	if(SP(o)->tt == TT_MN) {
		return DP(o)->mn;
	}
	else if(SP(o)->tt == TT_FN) {
		knh_methodn_t mn = FIELDN_UNMASK(DP(o)->fn);
		if(knh_Token_isGetter(o)) {
			return mn | KNH_FLAG_MN_GETTER;
		}
		else if(knh_Token_isSetter(o)) {
			return mn | KNH_FLAG_MN_SETTER;
		}
		return mn;
	}
	else {
		DEBUG3_ASSERT(SP(o)->tt == TT_NAME || SP(o)->tt == TT_CMETHODN || SP(o)->tt == TT_MT);
		knh_bytes_t name = knh_Token_tobytes(o);
		knh_index_t idx = knh_bytes_rindex(name, '.');
		knh_methodn_t mn ;

		if(idx != -1) {
			name = knh_bytes_last(name, idx+1);
		}

		mn = knh_tName_getMethodn(ctx, name, METHODN_NEWID);
		if(knh_Token_isGetter(o)) {
			return mn | KNH_FLAG_MN_GETTER;
		}
		if(knh_Token_isSetter(o)) {
			return mn | KNH_FLAG_MN_SETTER;
		}
		if(SP(o)->tt == TT_MT) {
			return mn | KNH_FLAG_MN_MOVTEXT;
		}
		return mn;
	}
}

/* ------------------------------------------------------------------------ */

static
Term *knh_StmtCALL_typing(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_class_t reqc)
{
	DEBUG3_ASSERT(DP(stmt)->size > 1);
	knh_methodn_t mn = knh_Token_tomethodn(ctx, DP(stmt)->tokens[0]);
	knh_type_t    btype   = TYPE_void;
	knh_class_t   mtd_cid = CLASS_Object;
	Method *mtd = (Method*)KNH_NULL;

	if(IS_Token(DP(stmt)->tokens[1])) {
		Token *tk_base = DP(stmt)->tokens[1];
		KNH_ASSERT(IS_Token(tk_base));

		if(SP(tk_base)->tt == TT_ASIS) {   /* (func _ ...) */
			if(mn == METHODN_typeof) {
				if(DP(stmt)->size == 3) {
					knh_Stmt_terms_typing(ctx, stmt, 2, cpr, ns, CLASS_Any);
					return TM(new_TokenCONST(ctx, FL(stmt), UP(new_Class__type(ctx, knh_Term_get_type(DP(stmt)->terms[2])))));
				}
				else {
					knh_Compiler_perror(ctx, cpr, KMSG_ETYPEOF, NULL);
					return knh_Stmt_foundTypeError(ctx, stmt, cpr);
				}
			}else if(mn == METHODN_default) {
				if(DP(stmt)->size == 3) {
					if(IS_Token(DP(stmt)->tokens[2]) && SP(DP(stmt)->tokens[2])->tt == TT_TYPEN) {
						knh_class_t cid = knh_NameSpace_getClass(ctx, ns, knh_Token_tobytes(DP(stmt)->tokens[2]));
						if(cid == CLASS_unknown) {
							knh_Token_perror(ctx, DP(stmt)->tokens[2], KMSG_UCLASSN);
							return knh_Stmt_foundTypeError(ctx, stmt, cpr);
						}
						return TM(new_TokenCONST(ctx, FL(stmt), knh_tClass_defaultValue(ctx, cid)));
					}
					else {
						knh_Stmt_terms_typing(ctx, stmt, 2, cpr, ns, CLASS_Any);
						return TM(new_TokenCONST(ctx, FL(stmt), knh_tClass_defaultValue(ctx, knh_Term_get_cid(DP(stmt)->terms[2]))));
					}
				}
				else {
					knh_Compiler_perror(ctx, cpr, KMSG_EDEFAULT, NULL);
					return knh_Stmt_foundTypeError(ctx, stmt, cpr);
				}
			}
			else {
				char bufmn[CLASSNAME_BUFSIZ];
				knh_format_methodn(bufmn, sizeof(bufmn), mn);
				//DEBUG3("1. lookup function.. %s()", bufmn);
				mtd_cid = knh_NameSpace_getFuncClass(ctx, ns, B(bufmn));
				if(mtd_cid != CLASS_unknown) {
					DEBUG_ASSERT_cid(mtd_cid);
					knh_Token_setCONST(ctx, tk_base, UP(knh_tClass[mtd_cid].class_cid));
					mtd = knh_Class_getMethod(ctx, mtd_cid, mn);
					KNH_ASSERT(IS_NOTNULL(mtd));
					btype = CLASS_TONNTYPE(mtd_cid);
					goto L_TOMETHOD;
				}

				if(DP(cpr)->vars[0].fn == FIELDN_this) {;
					//DEBUG3("2. lookup this.%s()", bufmn);
					mtd_cid = TYPE_UNMASK_NN(DP(cpr)->vars[0].type);
					mtd = knh_Class_getMethod(ctx, mtd_cid, mn);
					if(IS_NOTNULL(mtd)) {
						knh_Token_setSFPIDX(tk_base, 0, DP(cpr)->vars[0].type);
						btype = DP(cpr)->vars[0].type;
						goto L_TOMETHOD;
					}
				}

				//DEBUG3("3. lookup script function %s()", bufmn);
				{
					Script *scr = knh_Compiler_getScript(ctx, cpr);
					mtd_cid = knh_Object_cid(scr);
					mtd = knh_Class_getMethod(ctx, mtd_cid, mn);
					if(IS_NULL(mtd)) {
						knh_Compiler_perror(ctx, cpr, KMSG_UMETHODN, bufmn);
					}
					knh_Token_setCONST(ctx, tk_base, UP(scr));
					btype = CLASS_TONNTYPE(mtd_cid);
				}
				goto L_TOMETHOD;
			}
		}

		/* (func Class ...) */

		if(SP(tk_base)->tt == TT_TYPEN || SP(tk_base)->tt == TT_CID) {
			mtd_cid = knh_Token_toclass(ctx, tk_base, CLASS_unknown, ns);
			if(mtd_cid != CLASS_unknown) {
//				if(knh_tClass_hasDefaultValue(mtd_cid)) {
					knh_Token_setCONST(ctx, tk_base, UP(knh_tClass[mtd_cid].class_cid));
					btype = CLASS_TONNTYPE(mtd_cid);
//				}
//				else {
//					knh_Compiler_perror(ctx, cpr, KMSG_USCLASSFUNC, CLASSN(mtd_cid));
//					knh_Token_perrata(ctx, tk_base, "null");
//					return new_TokenCONST(ctx, tk_base, KNH_NULL);   /* CONST  */
//				}

				mtd = knh_Class_getMethod(ctx, mtd_cid, mn);
				if(IS_NULL(mtd)) {
					char bufcm[CLASSNAME_BUFSIZ];
					knh_format_cmethodn(bufcm, sizeof(bufcm), mtd_cid, mn);
					knh_Compiler_perror(ctx, cpr, KMSG_UMETHODN, bufcm);
				}
				goto L_TOMETHOD;
			}
			else {
				if(knh_Token_isLCONSTN(tk_base)) {
					knh_Token_setLCONST(ctx, tk_base, ns);
				}
				else {
					knh_Token_perror(ctx, DP(stmt)->tokens[1], KMSG_UCLASSN);
					return knh_Stmt_foundTypeError(ctx, stmt, cpr);
				}
//				knh_Token_perrata(ctx, tk_base, "null");
//				return new_TokenCONST(ctx, tk_base, KNH_NULL);      /* CONST  */
			}
		}
	}

	DEBUG3_ASSERT(btype == TYPE_void);
	knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, CLASS_Any);
	btype = knh_Term_get_type(DP(stmt)->terms[1]);
	mtd_cid = TYPE_UNMASK_NN(btype);
	//DEBUG3("CALL mtd_cid=%s", CLASSN(mtd_cid));
	mtd = knh_Class_getMethod(ctx, mtd_cid, mn);
	if(mtd_cid != CLASS_Any && IS_NULL(mtd)) {
		char buf[CLASSNAME_BUFSIZ];
		knh_format_cmethodn(buf, sizeof(buf), mtd_cid, mn);
		knh_Compiler_perror(ctx, cpr, KMSG_UMETHODN, buf);
	}

	L_TOMETHOD:
	if(IS_NOTNULL(mtd)) {
		return knh_StmtPARAMS_typing(ctx, stmt, cpr, ns, btype, mtd);
	}
	else {
		int i;
		for(i = 2; i < DP(stmt)->size; i++) {
			knh_Stmt_terms_typing(ctx, stmt, i, cpr, ns, TYPE_Any);
		}
		knh_Token_setMETHOD(ctx, DP(stmt)->tokens[0], mn, mtd, TYPE_Any);
		knh_Stmt_setType(stmt, TYPE_Any);
		return UP(stmt);
	}
}

/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */

static
knh_class_t knh_Stmt_infer_opAdd(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_class_t reqc)
{
	int i, has_Int = 0, has_Int64 = 0;
	knh_class_t bcid;
	DEBUG_ASSERT_cid(reqc);
	reqc = knh_tClass[reqc].bcid;
	for(i = 1; i < DP(stmt)->size; i++) {
		if(reqc == CLASS_String) {
			knh_Stmt_terms_typing(ctx, stmt, i, cpr, ns, CLASS_Any);
			continue;
		}
		else {
			knh_Stmt_terms_typing(ctx, stmt, i, cpr, ns, CLASS_Any);
			bcid = knh_Term_get_bcid(DP(stmt)->terms[i]);
			//DEBUG3("OP[+%d] %s", i, CLASSN(bcid));
			if(bcid == CLASS_String) { reqc = bcid; continue;}
			if(bcid == CLASS_Any) { reqc = bcid; continue; }
			if(bcid == CLASS_Any) continue;
			if(bcid == CLASS_Int) has_Int = 1;
			if(bcid == CLASS_Int64) has_Int64 = 1;
			if(bcid == CLASS_Float) { reqc = bcid;}
			if(reqc == CLASS_Float) continue;
			if(bcid == CLASS_Int64) { reqc = bcid;}
			if(reqc == CLASS_Int64) continue;
			if(bcid == CLASS_Int) { reqc = bcid; }
		}
	}
	if(reqc == CLASS_Float && (has_Int || has_Int64)) return CLASS_Number;
	if(reqc == CLASS_Int64 && has_Int) return CLASS_Number;
	return reqc;
}

/* ------------------------------------------------------------------------ */

static
knh_class_t knh_Stmt_infer_opEq(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns)
{
	knh_class_t bcid, bcid2;
	knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, CLASS_Any);
	bcid = knh_Term_get_bcid(DP(stmt)->terms[1]);
	knh_Stmt_terms_typing(ctx, stmt, 2, cpr, ns, CLASS_Any);
	bcid2 = knh_Term_get_bcid(DP(stmt)->terms[2]);
	if(bcid == bcid2) return bcid;
	if(bcid == CLASS_Any || bcid2 == CLASS_Any) return CLASS_Object;

	if(bcid == CLASS_Float) {
		if(bcid2 == CLASS_Int || bcid2 == CLASS_Int64) return CLASS_Number;
	}
	if(bcid2 == CLASS_Float) {
		if(bcid == CLASS_Int || bcid == CLASS_Int64) return CLASS_Number;
	}

	if(bcid == CLASS_Int64) {
		if(bcid2 == CLASS_Int) return CLASS_Number;
	}

	if(bcid2 == CLASS_Int64) {
		if(bcid == CLASS_Int) return CLASS_Number;
	}
	DEBUG3("CANNOT COMPARE %s %s", CLASSN(bcid), CLASSN(bcid2));
	return CLASS_Object;
}

/* ------------------------------------------------------------------------ */

Term *knh_StmtOP_typing(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_class_t reqc)
{
	DEBUG3_ASSERT(DP(stmt)->size > 1);
	size_t opsize = DP(stmt)->size - 1;
	Token *optk = DP(stmt)->tokens[0];
	knh_methodn_t mn = knh_token_tomethodn(SP(optk)->tt);
	DEBUG3_ASSERT(mn != METHODN_NONAME);
	knh_class_t   mtd_cid = CLASS_unknown;
	int typechecked = 0;

	switch(mn) {
	case METHODN_opAdd:
	{
		if(opsize == 1) {
			knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, reqc);
			return DP(stmt)->terms[1];
		}
		else if(opsize == 2) {
			mn = METHODN_opAdd__2;
		}
		mtd_cid = knh_Stmt_infer_opAdd(ctx, stmt, cpr, ns, reqc);;
		typechecked = 1;
	}
	break;

	case METHODN_opSub:
	{
		if(opsize == 1) {
			knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, reqc);
			mn = METHODN_opNeg;
		}
		else if(opsize == 2) {
			mn = METHODN_opSub__2;
		}
		mtd_cid = knh_Stmt_infer_opAdd(ctx, stmt, cpr, ns, reqc);;
		typechecked = 1;
	}
	break;

	case METHODN_opMul:
	{
		if(opsize == 1) {
			knh_Compiler_perror(ctx, cpr, KMSG_EOP2, knh_token_tochar(SP(optk)->tt));
			return knh_Stmt_foundTypeError(ctx, stmt, cpr);
		}
		else if(opsize == 2) {
			mn = METHODN_opMul__2;
		}
		mtd_cid = knh_Stmt_infer_opAdd(ctx, stmt, cpr, ns, reqc);;
		typechecked = 1;
	}
	break;

	case METHODN_opDiv:
	{
		if(opsize == 1) {
			knh_Compiler_perror(ctx, cpr, KMSG_EOP2, knh_token_tochar(SP(optk)->tt));
			return knh_Stmt_foundTypeError(ctx, stmt, cpr);
		}
		else if(opsize == 2) {
			mn = METHODN_opDiv__2;
		}
		mtd_cid = knh_Stmt_infer_opAdd(ctx, stmt, cpr, ns, reqc);;
		typechecked = 1;
	}
	break;

	case METHODN_opLogicalOr:
	{
		if(opsize == 1) {
			mn = METHODN_getSize;
			knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, CLASS_Any);
			mtd_cid = knh_Term_get_cid(DP(stmt)->terms[1]);
			typechecked = 1;
		}
		else {
			mtd_cid = CLASS_Int;
		}
	}
	break;

	case METHODN_opEq: case METHODN_opNeq:
	case METHODN_opGt: case METHODN_opGte:
	case METHODN_opLt: case METHODN_opLte:
	{
		if(opsize == 1) {
			knh_Compiler_perror(ctx, cpr, KMSG_EOP2, knh_token_tochar(SP(optk)->tt));
			return knh_Stmt_foundTypeError(ctx, stmt, cpr);
		}

		mtd_cid = knh_Stmt_infer_opEq(ctx, stmt, cpr, ns);

		if(mtd_cid == CLASS_Nue) {
			char bufcc[CLASSNAME_BUFSIZ];
			knh_snprintf(bufcc, sizeof(bufcc), "%s %s %s",
					CLASSN(knh_Term_get_cid(DP(stmt)->terms[1])),
					knh_token_tochar(SP(optk)->tt),
					CLASSN(knh_Term_get_cid(DP(stmt)->terms[2])));
			knh_Compiler_perror(ctx, cpr, KMSG_EOPCMP, bufcc);
			knh_Token_perrata(ctx, optk, "false");
			return TM(new_TokenCONST(ctx, FL(optk), KNH_FALSE)); /* CONST */
		}
	}
	typechecked = 1;
	break;

	case METHODN_opIn:
		if(opsize == 2) {
			Term *temp = DP(stmt)->terms[1];
			DP(stmt)->terms[1] = DP(stmt)->terms[2];
			DP(stmt)->terms[2] = temp;
			knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, CLASS_Any);
			mtd_cid = TYPE_UNMASK_NN(knh_Term_get_cid(DP(stmt)->terms[1]));
			typechecked = 0;
		}
		else {
			knh_Compiler_perror(ctx, cpr, KMSG_UOP2, knh_token_tochar(SP(optk)->tt));
			return knh_Stmt_foundTypeError(ctx, stmt, cpr);
		}

	default:
		knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, CLASS_Any);
		mtd_cid = TYPE_UNMASK_NN(knh_Term_get_cid(DP(stmt)->terms[1]));
		typechecked = 0;
	}

	KNH_ASSERT(mtd_cid != CLASS_unknown);
	{
		Method *mtd = knh_Class_getMethod(ctx, mtd_cid, mn);
		if(IS_NULL(mtd)) {
			//DEBUG3("mtd_cid=%s", CLASSN(mtd_cid));
			knh_Compiler_perror(ctx, cpr, KMSG_UOP, knh_token_tochar(SP(optk)->tt));
			return knh_Stmt_foundTypeError(ctx, stmt, cpr);
		}
		//DEBUG3("typechecked=%d",typechecked);
		if(typechecked == 0) {
			knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, mtd_cid);
			return knh_StmtPARAMS_typing(ctx, stmt, cpr, ns, mtd_cid, mtd);
		}
		else {
			knh_type_t rtype = knh_pmztype_totype(ctx, knh_Method_rtype(mtd), mtd_cid);
			if(knh_StmtCALL_isCONST(stmt, mtd)) {
				return knh_StmtCALL_toCONST(ctx, stmt, mtd);
			}
			knh_Token_setMETHOD(ctx, DP(stmt)->tokens[0], mn, mtd, rtype);
			knh_Stmt_setType(stmt, rtype);
		}
	}
	//DEBUG3("rtype=%s%s", TYPEQN(DP(stmt)->type));
	return TM(stmt);
}

/* ------------------------------------------------------------------------ */

static
Term *knh_StmtNEW_typing(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_class_t reqc)
{
	DEBUG3_ASSERT(DP(stmt)->size > 1);
	//knh_flag_t flag = knh_Stmt_metaflag__new(ctx, stmt);
	knh_methodn_t mn = knh_Token_tomethodn(ctx, DP(stmt)->tokens[0]);
	knh_class_t   mtd_cid;
	Method *mtd = (Method*)KNH_NULL;

	//DEBUG3("reqc=%s", CLASSN(reqc));
	if(knh_Token_isExceptionType(DP(stmt)->tokens[1])) {
		mtd_cid = CLASS_Exception;
		TODO();
	}
	else {
		mtd_cid = knh_Token_toclass(ctx, DP(stmt)->tokens[1], CLASS_unknown, ns);
	}

//	if(knh_Token_isArrayType(DP(stmt)->tokens[1])) {
//		if(mtd_cid == CLASS_unknown || mtd_cid == CLASS_Any) {
//			mtd_cid = CLASS_Array;
//		}
//		else {
//			mtd_cid = knh_pmzclass(ctx, CLASS_Array, mtd_cid, CLASS_Any);
//		}
//	}

	if(mtd_cid == CLASS_unknown) {
		knh_Token_perror(ctx, DP(stmt)->tokens[1], KMSG_UCLASSN);
		knh_Token_perrata(ctx, DP(stmt)->tokens[0], "null");
		return TM(new_TokenCONST(ctx, FL(DP(stmt)->tokens[1]), KNH_NULL));
	}

	if(mtd_cid == CLASS_Array) {
		DEBUG_ASSERT_cid(reqc);
		if(knh_tClass[reqc].bcid == CLASS_Array) {
			mtd_cid = reqc;
			knh_notice_TypeInf(ctx, SP(stmt)->fileid, SP(stmt)->line, "[]", mtd_cid);
		}
		else if(reqc == CLASS_Any && mn == METHODN_new__init && DP(stmt)->size > 2) {
			knh_Stmt_terms_typing(ctx, stmt, 2, cpr, ns, CLASS_Any);
			knh_type_t icid = knh_Term_get_cid(DP(stmt)->terms[2]);
			if(icid != CLASS_Any) {
				int i;
				for(i = 3; i < DP(stmt)->size; i++) {
					knh_Stmt_terms_typing(ctx, stmt, i, cpr, ns, CLASS_Any);
					if(icid != knh_Term_get_cid(DP(stmt)->terms[i])) {
						icid = CLASS_Any;
						break;
					}
				}
				if(icid != CLASS_Any) {
					mtd_cid = knh_class_Array(ctx, icid);
					knh_notice_TypeInf(ctx, SP(stmt)->fileid, SP(stmt)->line, "[]", mtd_cid);
				}
			}
		}
	}

	mtd = knh_Class_getMethod(ctx, mtd_cid, mn);
	if(IS_NULL(mtd)) {
		char bufnw[CLASSNAME_BUFSIZ];
		knh_snprintf(bufnw, sizeof(bufnw), "%s %s(...)", knh_Token_tochar(DP(stmt)->tokens[0]), CLASSN(mtd_cid));
		knh_Compiler_perror(ctx, cpr, KMSG_UNEW, bufnw);
		knh_perrata(ctx, DP(cpr)->fileid, DP(cpr)->line, bufnw, "null");
		return TM(new_TokenCONST(ctx, FL(DP(stmt)->tokens[0]), KNH_NULL));
	}

	DEBUG_ASSERT_cid(mtd_cid);
	KNH_SETv(ctx, DP(stmt)->terms[1], new_TokenCONST(ctx, FL(DP(stmt)->terms[0]), UP(knh_tClass[mtd_cid].class_cid)));
	//DEBUG3("Typing for %s", CLASSN(mtd_cid));
	knh_StmtPARAMS_typing(ctx, stmt, cpr, ns, CLASS_TONNTYPE(mtd_cid), mtd);
	return TM(stmt);
}

/* ------------------------------------------------------------------------ */

static
void knh_Token_setMAPPER(Ctx *ctx, Token *o, Mapper *mpr, knh_type_t exprt)
{
	KNH_ASSERT(SP(o)->tt == TT_TYPEN || SP(o)->tt == TT_CID);
	SP(o)->tt = TT_MTDMPR;
	DP(o)->mn = METHODN_NONAME;
	KNH_SETv(ctx, DP(o)->data, mpr);
	if(TYPE_ISNOTNULL(exprt) && knh_Mapper_isTotal(mpr)) {
		DP(o)->type = CLASS_TONNTYPE(DP(mpr)->tcid);
	}
	else {
		DP(o)->type = DP(mpr)->tcid;
	}
}

/* ------------------------------------------------------------------------ */
/* [MAPCAST] */

static
Term *knh_StmtMAPCAST_typing(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_class_t reqc)
{
	DEBUG3_ASSERT(DP(stmt)->size > 1);

	knh_class_t mprcid = knh_Token_toclass(ctx, DP(stmt)->tokens[0], CLASS_unknown, ns);
	if(mprcid == CLASS_unknown) {
		knh_Token_perror(ctx, DP(stmt)->tokens[1], KMSG_UCLASSN);
		knh_Token_perrata(ctx, DP(stmt)->tokens[0], CLASSN(reqc));
		mprcid = reqc;
	}
	if(mprcid == CLASS_Any) {
		mprcid = reqc;
		if(mprcid == CLASS_Any) {
			DEBUG3("Ignoring MAPCAST");
			knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, mprcid);
			return DP(stmt)->terms[1];
		}
	}

	knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, mprcid);

	if(DP(stmt)->size == 2) {
		L_MAP1:;
		knh_type_t exprt = knh_Term_get_cid(DP(stmt)->terms[1]);
		knh_type_t exprc = TYPE_UNMASK_NN(exprt);
		if(knh_class_instanceof(mprcid, exprc)) {
			DEBUG3("UPCAST (%s)%s", CLASSN(mprcid), CLASSN(exprc));
			return DP(stmt)->terms[1];
		}
		else {
			Mapper *mpr = knh_tMapper_find(ctx, exprc, mprcid);
			if(knh_Mapper_isNoSuchMapping(mpr)) {
				char bufcc[CLASSNAME_BUFSIZ*2];
				knh_snprintf(bufcc, sizeof(bufcc), "%s -> %s", CLASSN(exprc), CLASSN(mprcid));
				knh_Compiler_perror(ctx, cpr, KMSG_UMAP, bufcc);
				knh_Token_setMAPPER(ctx, DP(stmt)->tokens[0], mpr, exprt);
				knh_Stmt_setType(stmt, TYPE_void);
				return TM(stmt);
			}
			if(knh_Mapper_isStatic(mpr) && knh_Term_isCONST(DP(stmt)->terms[1])) {
				DEBUG3("MAPCAST TO CONST ..");
				KNH_LPUSH(ctx, DP(DP(stmt)->tokens[1])->data);
				KNH_SMAP(ctx, mpr);
				VM_SHIFT(ctx, -1);
				return TM(new_TokenCONST(ctx, FL(stmt), ctx->ebp[1].o));
			}
			knh_Token_setMAPPER(ctx, DP(stmt)->tokens[0], mpr, exprt);
			knh_Stmt_setType(stmt, CLASS_TONNTYPE(mprcid));
			return TM(stmt);
		}
	}
	else {
		TODO();
		goto L_MAP1;
	}
}

/* ------------------------------------------------------------------------ */
/* [MT] */

static
Term *knh_StmtMT_typing(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_class_t reqc)
{
	DEBUG3_ASSERT(DP(stmt)->size > 1);
	knh_methodn_t mn = knh_Token_tomethodn(ctx, DP(stmt)->tokens[0]);
	knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, CLASS_Any);
	knh_Token_setMETHOD(ctx, DP(stmt)->tokens[0], mn, (Method*)KNH_NULL, NNTYPE_String);
	knh_Stmt_setType(stmt, NNTYPE_String);
	return TM(stmt);
}

/* ------------------------------------------------------------------------ */

static
Term *knh_StmtAND_typing(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_class_t reqc)
{
	int i;
	for(i = 0; i < DP(stmt)->size; i++) {
		knh_Stmt_terms_typing(ctx, stmt, i, cpr, ns, CLASS_Boolean);
	}
	knh_Stmt_setType(stmt, NNTYPE_Boolean);
	return TM(stmt);
}

/* ------------------------------------------------------------------------ */

static
Term *knh_StmtOR_typing(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_class_t reqc)
{
	int i;
	for(i = 0; i < DP(stmt)->size; i++) {
		knh_Stmt_terms_typing(ctx, stmt, i, cpr, ns, CLASS_Boolean);
	}
	knh_Stmt_setType(stmt, NNTYPE_Boolean);
	return TM(stmt);
}

/* ------------------------------------------------------------------------ */

static
Term *knh_StmtALT_typing(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_class_t reqc)
{
	DEBUG3_ASSERT(DP(stmt)->size > 1);
	int i;
	knh_Stmt_terms_typing(ctx, stmt, 0, cpr, ns, reqc);
//	if(reqc == CLASS_Any) {
//		reqc = knh_Term_get_cid(DP(stmt)->terms[0]);
//	}
	for(i = 1; i < DP(stmt)->size; i++) {
		knh_Stmt_terms_typing(ctx, stmt, i, cpr, ns, reqc);
	}
	knh_Stmt_setType(stmt, reqc);
	return TM(stmt);
}

/* ------------------------------------------------------------------------ */

static
Term *knh_StmtTRINARY_typing(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_class_t reqc)
{
	DEBUG3_ASSERT(DP(stmt)->size == 3);
	knh_Stmt_terms_typing(ctx, stmt, 0, cpr, ns, CLASS_Boolean);
	knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, reqc);
//	if(reqc == CLASS_Any) {
//		reqc = knh_Term_get_cid(DP(stmt)->terms[1]);
//	}
	knh_Stmt_terms_typing(ctx, stmt, 2, cpr, ns, reqc);
	if(knh_Term_isCONST(DP(stmt)->terms[0])) {
		if(IS_TRUE(DP(DP(stmt)->tokens[0])->data)) {
			return DP(stmt)->terms[1];
		}
		else {
			return DP(stmt)->terms[2];
		}
	}
	knh_Stmt_setType(stmt, reqc);
	return TM(stmt);
}

/* ======================================================================== */

Term *knh_Stmt_typing(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_class_t reqc)
{
	knh_stmt_t stt = SP(stmt)->stt;
	switch(stt) {
	case STT_CALL1:
		return knh_StmtCALL1_typing(ctx, stmt, cpr, ns, reqc);
	case STT_CALL:
		return knh_StmtCALL_typing(ctx, stmt, cpr, ns, reqc);
	case STT_NEW:
		return knh_StmtNEW_typing(ctx, stmt, cpr, ns, reqc);
	case STT_OP:
		return knh_StmtOP_typing(ctx, stmt, cpr, ns, reqc);
	case STT_MAPCAST:
		return knh_StmtMAPCAST_typing(ctx, stmt, cpr, ns, reqc);
	case STT_MT:
		return knh_StmtMT_typing(ctx, stmt, cpr, ns, reqc);
	case STT_AND:
		return knh_StmtAND_typing(ctx, stmt, cpr, ns, reqc);
	case STT_OR:
		return knh_StmtOR_typing(ctx, stmt, cpr, ns, reqc);
	case STT_ALT:
		return knh_StmtALT_typing(ctx, stmt, cpr, ns, reqc);
	case STT_TRINARY:
		return knh_StmtTRINARY_typing(ctx, stmt, cpr, ns, reqc);

	default:
		if(reqc != CLASS_Any) {
			DEBUG3("undefined stmt=%s", knh_stmt_tochar(stt));
		}
	}
	return (Term*)stmt;
}

/* ------------------------------------------------------------------------ */

void knh_Stmt_terms_typing(Ctx *ctx, Stmt *stmt, size_t n, Compiler *cpr, NameSpace *ns, knh_class_t reqc)
{
	if(SP(stmt)->stt == STT_ERR) {
		DEBUG3("@SKIPED: This statement is STT_ERR");
		return;
	}

	if(knh_Term_isTyped(DP(stmt)->terms[n])) {
		//DEBUG3("@SKIPED: Already Typed !!");
		return;
	}

	if(IS_Token(DP(stmt)->tokens[n])) {
		/* knh_Token_const.c */
		Token *tk = DP(stmt)->tokens[n];
		if(SP(tk)->tt == TT_NAME) {
			if(knh_Token_isSystemVariable(tk)) {
				knh_Token_constSystemVariable(ctx, tk, cpr);
			}
			else {
				knh_TokenNAME_typing(ctx, tk, cpr);
			}
		}
		else if(SP(tk)->tt == TT_FN) {
			knh_TokenNAME_typing(ctx, tk, cpr);
		}
		else {
			tk = knh_Token_const(ctx, DP(stmt)->tokens[n], reqc, ns);
			if(tk != DP(stmt)->tokens[n]) {
				KNH_SETv(ctx, DP(stmt)->tokens[n], tk);
			}
		}
	}
	else {
		Term *tm = knh_Stmt_typing(ctx, DP(stmt)->stmts[n], cpr, ns, reqc);
		if(tm != DP(stmt)->terms[n]) {
			KNH_ASSERT(knh_Term_isCONST(tm));
			KNH_SETv(ctx, DP(stmt)->stmts[n], tm);
		}
//		DBG2_(
//		if(!knh_Stmt_isTyped(DP(stmt)->stmts[n])){
//			DEBUG3("%s, n=%d, stt=%s", knh_stmt_tochar(SP(stmt)->stt), (int)n, knh_stmt_tochar(DP(DP(stmt)->stmts[n])->stt));
//		})
	}
}

/* ======================================================================== */
/* ======================================================================== */
/* [ASM] */

static
void KNH_ASM_PPUSH(Ctx *ctx, Compiler *cpr, Term *tm, int level)
{
	if(level == 0) {
		if(IS_Token(tm) && SP((Token*)tm)->tt == TT_SYSCONST) {
			KNH_ASM_PUT_SYSCONST(ctx, cpr, DP((Token*)tm)->index);
		}
		else {
			KNH_ASM_MOVE(ctx, cpr, 0, tm);
		}
	}
	else {
		KNH_ASM_PUSH(ctx, cpr, tm);
	}
}

/* ======================================================================== */
/* [ASM] */

/* ------------------------------------------------------------------------ */

void KNH_ASM_THROWERR__T(Ctx *ctx, Compiler *cpr, char *text)
{
	KNH_ASM_THROW__OBJ(ctx, cpr, (Object*)new_String__T(ctx, text));
	knh_Compiler_setStopped(cpr, 1);
}

/* ------------------------------------------------------------------------ */

void KNH_ASM_THROWERR__S(Ctx *ctx, Compiler *cpr, String *msg)
{
	KNH_ASM_THROW__OBJ(ctx, cpr, (Object*)msg);
	knh_Compiler_setStopped(cpr, 1);
}

/* ------------------------------------------------------------------------ */

static
void knh_Stmt_perrorEVOID(Ctx *ctx, Stmt *stmt, size_t n)
{
	switch(SP(stmt)->stt) {
	case STT_CALL:
	case STT_NEW:
		{
			char bufnp[CLASSNAME_BUFSIZ];
			Method *mtd = DP(DP(stmt)->tokens[0])->mtd;
			DEBUG3_ASSERT(IS_Method(mtd));
			knh_format_methodparam(bufnp, sizeof(bufnp), DP(mtd)->mn, n - 1);
			knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_EVOIDPARAM, bufnp);
		}
		break;
	case STT_RETURN:
		if(n == 0) {
			knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_EVOIDRETURN, NULL);
		}
		else {
			char bufn[40];
			knh_snprintf(bufn, sizeof(bufn), "..., #%d", (int)(n + 1));
			knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_EVOIDRETURN, bufn);
		}
		break;

	case STT_LET:
		knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_EVOIDLET, NULL);
		break;

	default :
		knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_ETYPE, NULL);

	}
}

/* ------------------------------------------------------------------------ */

static
void knh_Stmt_perrorWNULL(Ctx *ctx, Stmt *stmt, size_t n)
{
	switch(SP(stmt)->stt) {
	case STT_CALL:
	case STT_NEW:
		{
			char bufnp[CLASSNAME_BUFSIZ];
			Method *mtd = DP(DP(stmt)->tokens[0])->mtd;
			DEBUG3_ASSERT(IS_Method(mtd));
			knh_format_methodparam(bufnp, sizeof(bufnp), DP(mtd)->mn, n - 1);
			knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_WNULLPARAM, bufnp);
		}
		break;
	case STT_RETURN:
		if(n == 0) {
			knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_WNULLRETURN, NULL);
		}
		else {
			char bufn[40];
			knh_snprintf(bufn, sizeof(bufn), "..., #%d", (int)(n + 1));
			knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_WNULLRETURN, bufn);
		}
		break;
	case STT_LET:
		knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_WNULLLET, NULL);
		break;

	default :
		knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_WNULL, NULL);
	}
}

/* ------------------------------------------------------------------------ */

static
void knh_Stmt_perrorENULL(Ctx *ctx, Stmt *stmt, size_t n)
{
	switch(SP(stmt)->stt) {
	case STT_CALL:
	case STT_NEW:
		{
			char bufnp[CLASSNAME_BUFSIZ];
			Method *mtd = DP(DP(stmt)->tokens[0])->mtd;
			DEBUG3_ASSERT(IS_Method(mtd));
			knh_format_methodparam(bufnp, sizeof(bufnp), DP(mtd)->mn, n - 1);
			knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_ENULLPARAM, bufnp);
		}
		break;
	case STT_RETURN:
		if(n == 0) {
			knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_ENULLRETURN, NULL);
		}
		else {
			char bufn[40];
			knh_snprintf(bufn, sizeof(bufn), "..., #%d", (int)(n + 1));
			knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_ENULLRETURN, bufn);
		}
		break;
	case STT_LET:
		knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_ENULLLET, NULL);
		break;

	default :
		knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_ENULL, NULL);
	}
}

/* ------------------------------------------------------------------------ */

static
void knh_Stmt_perrorETYPE(Ctx *ctx, Stmt *stmt, size_t n, knh_type_t reqt)
{
	switch(SP(stmt)->stt) {
	case STT_CALL:
	case STT_NEW:
		{
			char bufnp[CLASSNAME_BUFSIZ];
			Method *mtd = DP(DP(stmt)->tokens[0])->mtd;
			DEBUG3_ASSERT(IS_Method(mtd));
			knh_format_methodparam(bufnp, sizeof(bufnp), DP(mtd)->mn, n - 1);
			knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_ETYPEPARAM, bufnp);
		}
		break;
	case STT_RETURN:
		if(n == 0) {
			knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_ETYPERETURN, NULL);
		}
		else {
			char bufn[CLASSNAME_BUFSIZ];
			knh_snprintf(bufn, sizeof(bufn), "..., %s%s(#%d)", TYPEQN(reqt), (int)(n + 1));
			knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_ETYPERETURN, bufn);
		}
		break;
	case STT_LET:
		knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_ETYPELET, NULL);
		break;
	default :
		knh_perror(ctx, SP(stmt)->fileid, SP(stmt)->line, KMSG_ETYPE, NULL);
	}
}

/* ------------------------------------------------------------------------ */
/* [PUBLIC] */

static
void knh_StmtEXPR_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	KNH_ASSERT(knh_stmt_isExpr(SP(stmt)->stt));
	knh_stmt_cmpl(SP(stmt)->stt)(ctx, stmt, cpr, ns, reqt, level);
}

/* ------------------------------------------------------------------------ */

static
int knh_class_isImplictCastingTo(Ctx *ctx, knh_class_t varc, knh_class_t reqc)
{
	if(varc == CLASS_Any) return 1;
	if(varc == CLASS_Float) {
		return (reqc == CLASS_Int || reqc == CLASS_Int64);
	}
	if(varc == CLASS_Int) {
		return (reqc == CLASS_Float || reqc == CLASS_Int64);
	}
	if(varc == CLASS_Int64) {
		return (reqc == CLASS_Int || reqc == CLASS_Float);
	}
	return 0;
}

/* ------------------------------------------------------------------------ */

#define _knh_Stmt_terms_cmpl(ctx, stmt, n, cpr, ns, reqt, level)      knh_Stmt_terms_cmpl_(ctx, stmt, n, cpr, ns, reqt, level, 0)
#define _knh_Stmt_terms_cmplpush(ctx, stmt, n, cpr, ns, reqt, level)  knh_Stmt_terms_cmpl_(ctx, stmt, n, cpr, ns, reqt, level, 1)

void knh_Stmt_terms_cmpl_(Ctx *ctx, Stmt *stmt, int n, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level, int push)
{
	DEBUG3_ASSERT(n < DP(stmt)->size);

	if(!knh_Term_isTyped(DP(stmt)->terms[n])) {
		knh_Stmt_terms_typing(ctx, stmt, n, cpr, ns, TYPE_UNMASK_NN(reqt));
	}
	{
		Term *tm = DP(stmt)->terms[n];
		knh_class_t reqc = TYPE_UNMASK_NN(reqt);  /*tcid*/
		knh_type_t  vart = knh_Term_get_type(tm);
		knh_class_t varc = TYPE_UNMASK_NN(vart);   /*scid*/

		if(IS_Stmt(tm)) {
			knh_StmtEXPR_cmpl(ctx, DP(stmt)->stmts[n], cpr, ns, reqt, level);
			if(SP(stmt)->stt == STT_ERR) return;
		}

//		if(IS_Token(tm) && SP((Token*)tm)->tt == TT_SYSCONST) {
//			push = 1; /* always push */
//		}

		if(vart == TYPE_void) {
			knh_Stmt_perrorEVOID(ctx, stmt, n);
			KNH_ASM_THROWERR__S(ctx, cpr, TS_TYPEERR);
			return;
		}

		if(reqc == CLASS_Boolean) {
			Object *v = knh_Term_constValue(ctx, tm, CLASS_Any);
			if(IS_bInt(v)) {
				DEBUG3_ASSERT(IS_Token(DP(stmt)->tokens[n]));
				Token *tkc = DP(stmt)->tokens[n];
				knh_Token_perror(ctx, tkc, KMSG_WTRUEFALSE);
				if(((Int*)v)->value == 0) {
					knh_perrata(ctx, SP(tkc)->fileid, SP(tkc)->line, "0", "false");
					knh_Token_setCONST(ctx, DP(stmt)->tokens[n], KNH_FALSE);
				}
				else {
					char bufn[80];
					knh_snprintf(bufn, sizeof(bufn), "%d", (int)((Int*)v)->value);
					knh_perrata(ctx, SP(tkc)->fileid, SP(tkc)->line, bufn, "true");
					knh_Token_setCONST(ctx, DP(stmt)->tokens[n], KNH_TRUE);
				}
			}
			else if(IS_NULL(v) && knh_Term_isCONST(DP(stmt)->terms[n])) {
				knh_Token_setCONST(ctx, DP(stmt)->tokens[n], KNH_FALSE);
			}
			else if(!IS_Boolean(v) && IS_Token(tm)) {
				DEBUG3_ASSERT(IS_Token(DP(stmt)->tokens[n]));
				knh_Token_perror(ctx, DP(stmt)->tokens[n], KMSG_WTRUEFALSE);
				knh_Token_setCONST(ctx, DP(stmt)->tokens[n], KNH_TRUE);
			}
			if(IS_Token(tm) && push) {
				KNH_ASM_PPUSH(ctx, cpr, tm, level);
			}
			return ;
		}

		if(varc == CLASS_Nue) {
			if(TYPE_ISNOTNULL(reqt)) {
				knh_Stmt_perrorENULL(ctx, stmt, n);
				KNH_ASM_THROWERR__S(ctx, cpr, TS_NULLERR);
				return;
			}
			else {
				if(IS_Token(tm) && push) {
					KNH_ASM_PPUSH(ctx, cpr, tm, level);
				}
				return;
			}
		}

		if(reqc == CLASS_Any || reqc == varc || knh_class_instanceof(varc, reqc)) {
			if(TYPE_ISNOTNULL(reqt) && TYPE_ISNULLABLE(vart)) {
				knh_Stmt_perrorWNULL(ctx, stmt, n);
				KNH_ASM_NULLCHK(ctx, cpr, tm);
			}
			if(IS_Token(tm) && push) {
				KNH_ASM_PPUSH(ctx, cpr, tm, level);
			}
			return ;
		}

		if(varc == CLASS_Any) {
			DEBUG3("Implicit Casting  %s => %s", CLASSN(varc), CLASSN(reqc));
			if(IS_Token(tm)) {
				KNH_ASM_PPUSH(ctx, cpr, tm, level);
			}
			if(TYPE_ISNULLABLE(reqt)) {
				KNH_ASM_ANYMAP(ctx, cpr, reqc);
			}
			else {
				if(TYPE_ISNULLABLE(vart)) {
					knh_Stmt_perrorWNULL(ctx, stmt, n);
				}
				KNH_ASM_ANYMAPE(ctx, cpr, reqc);
			}
			KNH_SETv(ctx, DP(stmt)->terms[n], new_StmtDONE_type(ctx, reqt));
			return ;
		}

		if(knh_class_isImplictCastingTo(ctx, varc, reqc)) {
			DEBUG3("Implicit Casting  %s => %s", CLASSN(varc), CLASSN(reqc));
			Mapper *mpr = knh_tMapper_find(ctx, varc, reqc);
			if(IS_Token(tm)) {
				KNH_ASM_PPUSH(ctx, cpr, tm, level);
			}
			if(TYPE_ISNULLABLE(reqt)) {
				if(knh_Mapper_isFinal(mpr)) {
					KNH_ASM_SMAP(ctx, cpr, (Object*)mpr);
				}
				else {
					KNH_ASM_MAP(ctx, cpr, reqc);
				}
			}
			else {
				if(TYPE_ISNULLABLE(vart) || !knh_Mapper_isTotal(mpr)) {
					knh_Stmt_perrorWNULL(ctx, stmt, n);
				}
				if(knh_Mapper_isFinal(mpr)) {
					KNH_ASM_SMAPE(ctx, cpr, (Object*)mpr);
				}
				else {
					KNH_ASM_MAPE(ctx, cpr, reqc);
				}
			}
			KNH_SETv(ctx, DP(stmt)->terms[n], new_StmtDONE_type(ctx, reqt));
			return ;
		}

		DEBUG3("TYPEERROR!! %s <= %s", CLASSN(reqc), CLASSN(varc));
		knh_Stmt_perrorETYPE(ctx, stmt, n, reqt);
		KNH_ASM_THROWERR__S(ctx, cpr, TS_TYPEERR);
	}
}

/* ======================================================================== */
/* [CALL1] */

void knh_StmtCALL1_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	DEBUG3_ASSERT(DP(stmt)->size == 1);
	knh_Stmt_terms_cmplpush(ctx, stmt, 0, cpr, ns, reqt, level);
}

/* ------------------------------------------------------------------------ */

void knh_StmtCALL_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	Token *mtdtk = DP(stmt)->tokens[0];
	Object *value = knh_Term_constValueNULL(ctx, DP(stmt)->terms[1], CLASS_Any);
	//DEBUG3("tt = %s", knh_token_tochar(DP(mtdtk)->tt));
	DEBUG3_ASSERT(SP(mtdtk)->tt == TT_MTDMPR);
	Method *mtd = DP(mtdtk)->mtd;
	knh_class_t mtd_cid;   knh_type_t btype;

	int dynamic_binding = 0;
	if(IS_NOTNULL(mtd) && (!knh_Method_isFinal(mtd) && value == NULL)) {
		DEBUG3("Dynamic binding");
		KNH_SETv(ctx, DP(mtdtk)->data, KNH_NULL);
		dynamic_binding = 1;
	}

	KNH_ASM_PPUSH(ctx, cpr, UP(mtdtk), level);
	if(value != NULL && IS_Class(value)) {
		mtd_cid = ((Class*)value)->cid;
		btype = CLASS_TONNTYPE(mtd_cid);
		KNH_ASM_PUSH__DEF(ctx, cpr, mtd_cid);
	}
	else {
		btype   = knh_Term_get_type(DP(stmt)->terms[1]);
		mtd_cid = TYPE_UNMASK_NN(btype);
		if(IS_Method(mtd)) {
			//DEBUG3("mtd_cid=%s, mtd->cid=%s", CLASSN(mtd_cid), CLASSN(DP(mtd)->cid));
			knh_Stmt_terms_cmplpush(ctx, stmt, 1, cpr, ns, mtd_cid, level + 1);
		}
		else {
			//DEBUG3("mtd_cid=%s, mtd->cid=%s", CLASSN(mtd_cid), CLASSN(DP(mtd)->cid));
			knh_Stmt_terms_cmplpush(ctx, stmt, 1, cpr, ns, CLASS_Any, level + 1);
		}
		if(SP(stmt)->stt == STT_ERR) { return ; }
	}
	if(IS_NULL(mtd)) {
		int i;
		for(i = 2; i < DP(stmt)->size; i++) {
			knh_Stmt_terms_cmplpush(ctx, stmt, 1, cpr, ns, CLASS_Any, level + 2);
		}
		KNH_ASM_DCALL(ctx, cpr, DP(stmt)->size - 1, DP(mtdtk)->mn);
	}
	else {
		int i;
		knh_class_t base_cid = TYPE_UNMASK_NN(btype);
		if(knh_Method_isNullBase(mtd)) btype = base_cid;
		DEBUG3("base_cid=%s", CLASSN(base_cid));
		for(i = 2; i < DP(stmt)->size; i++) {
			knh_type_t reqt = knh_pmztype_totype(ctx, knh_Method_ptype(mtd, i - 2), base_cid);
			//DEBUG3("***base_cid=%s,reqt=%s%s", CLASSN(base_cid), TYPEQN(reqt));
			knh_Stmt_terms_cmplpush(ctx, stmt, i, cpr, ns, reqt, level + i);
			if(SP(stmt)->stt == STT_ERR) { return ;}
		}

		if(dynamic_binding) {
			KNH_ASM_CALL(ctx, cpr, DP(stmt)->size - 1, DP(mtd)->mn);
		}
		else {
			if(knh_Method_isNoSuchMethod(mtd)) {
				KNH_ASM_DCALL(ctx, cpr, DP(stmt)->size - 1, DP(mtd)->mn);
			}
			else if(TYPE_ISNOTNULL(btype) || knh_Method_isNullBase(mtd)) {
				KNH_ASM_SCALL(ctx, cpr, DP(stmt)->size - 1);
			}
			else {
				KNH_ASM_NSCALL(ctx, cpr, DP(stmt)->size - 1);
			}
		}
	}
}

/* ======================================================================== */
/* [OP] */

void knh_StmtOP_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	int i;
	Token *mtdtk = DP(stmt)->tokens[0];
	DEBUG3_ASSERT(SP(mtdtk)->tt == TT_MTDMPR);
	Method *mtd = DP(mtdtk)->mtd;
	DEBUG3_ASSERT(IS_Method(mtd));
	SP(stmt)->stt = STT_CALL; /* for error message  */
	knh_type_t btype = knh_Term_get_type(DP(stmt)->terms[1]);
	knh_class_t base_cid = TYPE_UNMASK_NN(btype);
	if(knh_Method_isNullBase(mtd)) btype = base_cid;

	KNH_ASM_PPUSH(ctx, cpr, UP(mtdtk), level);
	knh_Stmt_terms_cmplpush(ctx, stmt, 1, cpr, ns, btype, level + 1);
	if(SP(stmt)->stt == STT_ERR) { return ; }

	for(i = 2; i < DP(stmt)->size; i++) {
		knh_type_t reqt = knh_pmztype_totype(ctx, knh_Method_ptype(mtd, i - 2), base_cid);
		knh_Stmt_terms_cmplpush(ctx, stmt, i, cpr, ns, reqt, level + i);
		if(SP(stmt)->stt == STT_ERR) { return ;}
	}
	if(TYPE_ISNOTNULL(btype) || knh_Method_isNullBase(mtd)) {
		KNH_ASM_SCALL(ctx, cpr, DP(stmt)->size - 1);
	}
	else {
		KNH_ASM_NSCALL(ctx, cpr, DP(stmt)->size - 1);
	}
}

/* ------------------------------------------------------------------------ */

void knh_StmtNEW_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	Token *mtdtk = DP(stmt)->tokens[0];
	Object *value = knh_Term_constValueNULL(ctx, DP(stmt)->terms[1], CLASS_Any);
	DEBUG3_ASSERT(SP(mtdtk)->tt == TT_MTDMPR);
	DEBUG3_ASSERT(value != NULL && IS_Class(value));
	Method *mtd = DP(mtdtk)->mtd;
	knh_class_t mtd_cid = ((Class*)value)->cid;
	int i;
	knh_flag_t flag = 0;

	KNH_ASM_PPUSH(ctx, cpr, UP(mtdtk), level);
	KNH_ASM_PUSH__OBJ(ctx, cpr, (Object*)KNH_NULL);
	for(i = 2; i < DP(stmt)->size; i++) {
		knh_type_t reqt = knh_pmztype_totype(ctx, knh_Method_ptype(mtd, i - 2), mtd_cid);
		knh_Stmt_terms_cmplpush(ctx, stmt, i, cpr, ns, reqt, level + i);
		if(SP(stmt)->stt == STT_ERR) { return ;}
	}
	KNH_ASM_NEW(ctx, cpr, DP(stmt)->size - 1, flag, mtd_cid);
}

/* ------------------------------------------------------------------------ */

void knh_StmtMT_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	Token *mtdtk = DP(stmt)->tokens[0];
	DEBUG3_ASSERT(SP(mtdtk)->tt == TT_MTDMPR);
	knh_Stmt_terms_cmplpush(ctx, stmt, 1, cpr, ns, CLASS_Any, level);
	KNH_ASM_MT(ctx, cpr, DP(mtdtk)->mn);
}

/* ------------------------------------------------------------------------ */

void knh_StmtMAPCAST_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	Token *tkf = DP(stmt)->tokens[0];
	if(DP(stmt)->size == 2) {
		L_MAP1:;
		Mapper *mpr = (Mapper*)DP(tkf)->data;
		DEBUG3_ASSERT(IS_Mapper(mpr));
		knh_Stmt_terms_cmplpush(ctx, stmt, 1, cpr, ns, DP(mpr)->scid, level);
		if(TYPE_ISNULLABLE(reqt)) {
			if(knh_Mapper_isFinal(mpr)) {
				KNH_ASM_SMAP(ctx, cpr, (Object*)mpr);
			}
			else {
				KNH_ASM_MAP(ctx, cpr, DP(mpr)->tcid);
			}
		}
		else {
			if(knh_Mapper_isFinal(mpr)) {
				KNH_ASM_SMAPE(ctx, cpr, (Object*)mpr);
			}
			else {
				KNH_ASM_MAPE(ctx, cpr, DP(mpr)->tcid);
			}
		}
	}
	else {
		TODO();
		goto L_MAP1;
	}
}

/* ======================================================================== */

#ifdef __cplusplus
}
#endif
