/****************************************************************************
 * 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

///* ------------------------------------------------------------------------ */
//
//static
//knh_cfield_t* knh_cfield_findNULL(Ctx *ctx,knh_cfield_t *cf, size_t max, knh_fieldn_t fn)
//{
//	knh_index_t idx;
//	for(idx = 0; idx < max; idx++) {
//		if(cf[idx].fn == fn) {
//			return cf + idx;
//		}
//		if(cf[idx].fn == FIELDN_NONAME) {
//			return NULL;
//		}
//	}
//	return NULL;
//}
//
///* ------------------------------------------------------------------------ */
//
//static
//knh_index_t knh_cfield_index(Ctx *ctx,knh_cfield_t *cf, size_t max, knh_fieldn_t fn)
//{
//	knh_index_t idx;
//	for(idx = 0; idx < max; idx++) {
//		if(cf[idx].fn == fn) {
//			return idx;
//		}
//		if(cf[idx].fn == FIELDN_NONAME) {
//			return -1;
//		}
//	}
//	return -1;
//}

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

static
knh_index_t knh_Compiler_cfield_add(Ctx *ctx, Compiler *cpr, knh_cfield_t *cf, size_t max, knh_flag_t flag, knh_type_t type, knh_fieldn_t fn, Object *value)
{
	knh_index_t idx;
	for(idx = 0; idx < max; idx++) {
		if(cf[idx].fn == FIELDN_NONAME) {
			cf[idx].flag = flag;
			cf[idx].fn   = fn;
			cf[idx].type = type;
			KNH_SETv(ctx, cf[idx].value, value);
			return idx;
		}
		if(cf[idx].fn == fn) {
			if(cf[idx].type != type) {
				char buf[CLASSNAME_BUFSIZ];
				knh_snprintf(buf, sizeof(buf), "%s%s %s", TYPEQN(cf->type), FIELDN(fn));
				knh_Compiler_perror(ctx, cpr, KMSG_DIFFDECL, buf);
				return -1;
			}
			return idx;
		}
	}
	{
		char buf[CLASSNAME_BUFSIZ];
		knh_snprintf(buf, sizeof(buf), "%s%s %s", TYPEQN(type), FIELDN(fn));
		knh_Compiler_perror(ctx, cpr, KMSG_TOOMANYVARS, buf);
		return -1;
	}
}

/* ------------------------------------------------------------------------ */
/* [variable] */

static
void knh_Compiler_declareScriptVariable(Ctx *ctx, Compiler *cpr, knh_flag_t flag, knh_type_t type, knh_fieldn_t fn, Object *value)
{
	knh_Script_t *scr = knh_Compiler_getScript(ctx, cpr);
	knh_class_t cid = knh_Object_cid(scr);
	if(TYPE_ISNOTNULL(type) && IS_NULL(value)) {
		knh_class_t cidt = TYPE_UNMASK_NN(type);
		value = knh_tClass_defaultValue(ctx, cidt);
		KNH_ASSERT(IS_NOTNULL(value));
	}
	{	
		knh_cfield_t *cf = DP(knh_tClass[cid].cstruct)->fields;
		knh_index_t idx = knh_Compiler_cfield_add(ctx, cpr, cf, KNH_SCRIPT_FIELDSIZE, flag, type, fn, value);
		KNH_ASSERT(DP(knh_tClass[cid].cstruct)->fsize == KNH_SCRIPT_FIELDSIZE);
		if(idx != -1) {
			//DEBUG3("idx=%d", idx);
			KNH_SETv(ctx, scr->fields[idx], value);
		}
	}
}

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

static
void knh_Compiler_declareVariable(Ctx *ctx, Compiler *cpr, knh_flag_t flag, knh_type_t type, knh_fieldn_t fn, Object *value)
{
	knh_index_t idx = knh_Compiler_cfield_add(ctx, cpr, DP(cpr)->vars, KONOHA_LOCALVAR_SIZE, flag, type, fn, value);
	if(idx != -1 && (idx + 1) > DP(cpr)->vars_size) {
		DP(cpr)->vars_size = idx + 1;
	}
}

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

static
knh_index_t knh_Compiler_indexOfVariable(Compiler *cpr, knh_fieldn_t fnq)
{
	knh_fieldn_t fn = FIELDN_UNMASK(fnq);
	knh_index_t idx = 0;
	for(idx = 0; idx < DP(cpr)->vars_size; idx++) {
		if(DP(cpr)->vars[idx].fn == fn) {
			return idx;
		}
		if(DP(cpr)->vars[idx].fn == FIELDN_NONAME) {
			return -1;
		}
	}
	return -1;
}

#define knh_Compiler_indexOfScriptVariable(cpr, fnq)       knh_Class_queryField(knh_Object_cid(knh_Compiler_getScript(ctx, cpr)), fnq)
#define knh_Compiler_indexOfFieldVariable(cpr, fnq)        knh_Class_queryField(DP(cpr)->this_cid, fnq)

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

knh_cfield_t *knh_Compiler_cfieldOfVariable(Compiler *cpr, knh_index_t idx)
{
	KNH_ASSERT(idx != -1 && idx < KONOHA_LOCALVAR_SIZE);
	KNH_ASSERT(DP(cpr)->vars[idx].fn != FIELDN_NONAME);
	return &(DP(cpr)->vars[idx]);
}

////#define _knh_Compiler_cfieldOfGlobalVariable(mc, idx)       knh_Class_cfield(knh_Object_cid(mc->nsproto), idx)
////#define _knh_Compiler_cfieldOfFieldVariable(mc, idx)        knh_Class_cfield(mc->this_cid, idx)
////#define _knh_Compiler_cfieldOfLocalVariable(mc, idx)        knh_Compiler_cfieldOfVariable(b, idx)

/* ======================================================================== */
/* [name] */

static
knh_fieldn_t knh_Token_tofnq(Ctx *ctx, Token *o)
{
	DEBUG3_ASSERT(IS_Token(o));
	if(DP(o)->tt == TT_FN) {
		return DP(o)->fn;
	}
	else {
		return knh_tName_get_fnq(ctx, knh_Token_tobytes(o), FIELDN_NEWID);
	}
}

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

Stmt *new_StmtDONE_type(Ctx *ctx, knh_type_t type)
{
	Stmt *stmt = new_Stmt(ctx, KNH_FLAG_STMTF_TYPED, STT_DONE);
	DP(stmt)->type = type;
	return stmt;
}

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

void knh_Token_setSFPIDX(Token *tk, int idx, knh_type_t type)
{
	KNH_ASSERT(DP(tk)->tt == TT_NAME || DP(tk)->tt == TT_FN || DP(tk)->tt == TT_ASIS);
	DP(tk)->tt = TT_SFPIDX;
	DP(tk)->index = idx;
	DP(tk)->type = type;
	knh_Token_setTyped(tk, 1);
}

///* ------------------------------------------------------------------------ */
//
//static
//void knh_Token_nameEBP(Token *tk, int idx, knh_type_t type)
//{
//	KNH_ASSERT(DP(tk)->tt == TT_NAME);
//	DP(tk)->tt = TT_EBPIDX;
//	DP(tk)->index = idx;
//	DP(tk)->type = type;
//}

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

static
void knh_Token_setFLDIDX(Token *tk, int idx, knh_type_t type)
{
	KNH_ASSERT(DP(tk)->tt == TT_NAME || DP(tk)->tt == TT_FN);
	DP(tk)->tt = TT_FLDIDX;
	DP(tk)->index = idx;
	DP(tk)->type = type;
	knh_Token_setTyped(tk, 1);
}

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

static
void knh_Token_setOBJIDX(Ctx *ctx, Token *tk, Object *value, int idx, knh_type_t type)
{
	KNH_ASSERT(DP(tk)->tt == TT_NAME || DP(tk)->tt == TT_FN);
	DP(tk)->tt = TT_OBJIDX;
	DP(tk)->index = idx;
	DP(tk)->type = type;
	KNH_SETv(ctx, DP(tk)->data, value);
	knh_Token_setTyped(tk, 1);
}


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

void knh_TokenNAME_typing(Ctx *ctx, Token *tk,  Compiler *cpr)
{
	knh_fieldn_t fnq = knh_Token_tofnq(ctx, tk);
	knh_index_t idx;
	
	if(FIELDN_IS_U2(fnq)) {
		goto L_GLOBAL;
	}
	
	if(FIELDN_IS_U1(fnq)) {
		goto L_FIELD;
	}

	idx = knh_Compiler_indexOfVariable(cpr, FIELDN_UNMASK(fnq));
	if(idx != -1) {
		knh_cfield_t *cf = knh_Compiler_cfieldOfVariable(cpr, idx);
		knh_type_t type = knh_pmztype_totype(ctx, cf->type, DP(cpr)->this_cid);
		//DEBUG3("local variable %s%s", TYPEQN(type));
		knh_Token_setSFPIDX(tk, idx, type);
		return ;
	}

	L_FIELD:;
	idx = knh_Class_queryField(DP(cpr)->this_cid, fnq);
	if(idx != -1) {
		knh_cfield_t *cf = knh_Class_fieldAt(DP(cpr)->this_cid, idx);
		knh_type_t type = knh_pmztype_totype(ctx, cf->type, DP(cpr)->this_cid);
		//DEBUG3("field variable %s%s", TYPEQN(type));
		knh_Token_setFLDIDX(tk, idx, type);
		return ;
	}
	
	L_GLOBAL:;
	{
		Script *scr = knh_Compiler_getScript(ctx, cpr);
		idx = knh_Class_queryField(knh_Object_cid(scr), fnq);
		if(idx != -1) {
			knh_cfield_t *cf = knh_Class_fieldAt(knh_Object_cid(scr), idx);
			knh_type_t type = knh_pmztype_totype(ctx, cf->type, knh_Object_cid(scr));
			//DEBUG3("script variable %s%s", TYPEQN(type));
			knh_Token_setOBJIDX(ctx, tk, (Object*)scr, idx, type);
			return ;
		}
	}
	knh_Token_perror(ctx, tk, KMSG_UVARN);
	{
		char bufn[CLASSNAME_BUFSIZ];
		knh_snprintf(bufn, sizeof(bufn), "Type!!: unknown variable '%s'", knh_Token_tochar(tk));
		knh_Token_setERR(ctx, tk, new_String(ctx, B(bufn), NULL));
	}
	return;
}

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

static
knh_bool_t knh_Compiler_existsName(Ctx *ctx, Compiler *cpr, knh_fieldn_t fnq)
{
	knh_index_t idx = -1;
	knh_fieldn_t fn = FIELDN_UNMASK(fnq);
	if(FIELDN_IS_U2(fnq)) {
		goto L_GLOBAL;
	}

	if(FIELDN_IS_U1(fnq)) {
		goto L_FIELD;
	}
	
	idx = knh_Compiler_indexOfVariable(cpr, fn);
	if(idx != -1) return 1;
	
	L_FIELD:;
	idx = knh_Class_queryField(DP(cpr)->this_cid, fnq);
	if(idx != -1) {
		return 1;
	}
	
	L_GLOBAL:;
	idx = knh_Class_queryField(knh_Object_cid(knh_Compiler_getScript(ctx, cpr)), fnq);
	if(idx != -1) {
		return 1;
	}
	return 0;
}

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

void knh_StmtDECL_toLET(Ctx *ctx, Stmt *stmt)
{
	DEBUG3_ASSERT(DP(stmt)->stt == STT_DECL);
	//DBG2_P("switching .. DECL => LET");
	DP(stmt)->stt = STT_LET;
	KNH_SETv(ctx, DP(stmt)->terms[0], DP(stmt)->terms[1]);
	KNH_SETv(ctx, DP(stmt)->terms[1], DP(stmt)->terms[2]);
	KNH_SETv(ctx, DP(stmt)->terms[2], KNH_NULL);
	DP(stmt)->size = 2;
}

/* ------------------------------------------------------------------------ */
/* [DECL] */

#ifndef StmtMACRO
#define StmtDECL_type(stmt)         DP(stmt)->tokens[0]
#define StmtDECL_name(stmt)         DP(stmt)->tokens[1]
#define StmtDECL_value(stmt)        DP(stmt)->terms[2]
#endif

void knh_StmtDECL_name(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, int level)
{
	knh_fieldn_t fnq = knh_Token_tofnq(ctx, StmtDECL_name(stmt));
	knh_fieldn_t fn  = FIELDN_UNMASK(fnq);
	knh_flag_t flag  = knh_Stmt_metaflag__field(ctx, stmt);
	knh_type_t pmztype  = knh_Token_totype(ctx, StmtDECL_type(stmt), CLASS_Any, ns);
	knh_type_t type = knh_pmztype_totype(ctx, pmztype, DP(cpr)->this_cid);
	knh_class_t var_cid = TYPE_UNMASK_NN(type);
	knh_Stmt_terms_typing(ctx, stmt, 2, cpr, ns, var_cid);

	DBG2_(
	if(pmztype != type) {
		DBG2_P("pmztype=%s%s type=%s%s", TYPEQN(pmztype), TYPEQN(type));
	})
	
	if(level == 0) {  /* SCRIPT VARIABLE */   
		flag = flag | KNH_FLAG_CFF_GETTER | KNH_FLAG_CFF_SETTER;
		if(FIELDN_IS_U1(fnq) && FIELDN_IS_U2(fnq)) {
			flag |= KNH_FLAG_CFF_PROTECTED;
		}
		if(knh_Term_isASIS(StmtDECL_value(stmt)) || knh_Term_isCONST(StmtDECL_value(stmt))) {
			Object *value = knh_Term_constValue(ctx, StmtDECL_value(stmt), var_cid);
			if(IS_NULL(value) && TYPE_ISNOTNULL(type)) {
				value = knh_tClass_defaultValue(ctx, var_cid);
			}
			knh_Compiler_declareScriptVariable(ctx, cpr, flag, type, fn, value);
			knh_Stmt_done(ctx, stmt);
		}
		else {
			knh_Compiler_declareScriptVariable(ctx, cpr, flag, type, fn, KNH_NULL);
			knh_StmtDECL_toLET(ctx, stmt);
		}
	}
	else if(level == 1) { /* FIELD VARIABLE */
		Object *value;
		if(FIELDN_IS_U1(fnq)) { 
			flag |= KNH_FLAG_CFF_PROTECTED;
		}
		else {
			flag |= KNH_FLAG_CFF_GETTER | KNH_FLAG_CFF_SETTER;
		}
		
		if(FIELDN_IS_U2(fnq)) {
			knh_Compiler_perror(ctx, cpr, KMSG_IGSCRIPTNAME, knh_Token_tochar(StmtDECL_name(stmt)));
			knh_Stmt_done(ctx, stmt);
			return;
		}

		if(knh_Term_isASIS(StmtDECL_value(stmt)) || knh_Term_isCONST(StmtDECL_value(stmt))) {
			value = knh_Term_constValue(ctx, StmtDECL_value(stmt), var_cid);
		}
		else {
			knh_Compiler_perror(ctx, cpr, KMSG_IGFIELDVALUE, FIELDN(fn));
			value = KNH_NULL;
		}
		if(IS_NULL(value) && TYPE_ISNOTNULL(type)) {
			value = knh_tClass_defaultValue(ctx, var_cid);
		}
		knh_Compiler_declareVariable(ctx, cpr, flag, type, fn, value);
		knh_Stmt_done(ctx, stmt);
	}
	else if(level == -1) {  /* level == -1 PARAM_VARIABLE */
		//type = pmztype;
		Object *value = KNH_NULL;
		if(FIELDN_IS_U2(fnq)) {
			knh_Compiler_perror(ctx, cpr, KMSG_IGSCRIPTNAME, knh_Token_tochar(StmtDECL_name(stmt)));
			fnq = fn;
		}
		if(FIELDN_IS_U1(fnq)) {
			knh_Compiler_perror(ctx, cpr, KMSG_IGFIELDNAME, knh_Token_tochar(StmtDECL_name(stmt)));
			fnq = fn;
		}
		if(knh_Term_isCONST(StmtDECL_value(stmt))) {
			value = knh_Term_constValue(ctx, StmtDECL_value(stmt), var_cid);
			if(IS_NOTNULL(value)) {
				if(DP(StmtDECL_type(stmt))->tt == TT_ASIS) {
					var_cid = knh_Object_cid(value);
					type = var_cid;
				}
				else {
					type = var_cid;
				}
			}
		}
		//DEBUG3("type=%s%s", TYPEQN(type));
		knh_Compiler_declareVariable(ctx, cpr, flag, type, fn, value);
	}
	else if(level == -2) {  /* level == -2 PARAM_LOCAL_VARIABLE */
		Object *value = KNH_NULL;
		if(FIELDN_IS_U2(fnq)) {
			knh_Compiler_perror(ctx, cpr, KMSG_IGSCRIPTNAME, knh_Token_tochar(StmtDECL_name(stmt)));
			fnq = fn;
		}
		if(FIELDN_IS_U1(fnq)) {
			knh_Compiler_perror(ctx, cpr, KMSG_IGFIELDNAME, knh_Token_tochar(StmtDECL_name(stmt)));
			fnq = fn;
		}
		if(knh_Term_isCONST(StmtDECL_value(stmt))) {
			value = knh_Term_constValue(ctx, StmtDECL_value(stmt), var_cid);
			if(IS_NOTNULL(value)) {
				if(DP(StmtDECL_type(stmt))->tt == TT_ASIS) {
					var_cid = knh_Object_cid(value);
					type = CLASS_TONNTYPE(var_cid);
				}
				else {
					type = CLASS_TONNTYPE(var_cid);
				}
			}
		}
		//DEBUG3("type=%s%s", TYPEQN(type));
		knh_Compiler_declareVariable(ctx, cpr, flag, type, fn, value);
	}
	else {  
		if(FIELDN_IS_U2(fnq)) {
			knh_Compiler_perror(ctx, cpr, KMSG_IGSCRIPTNAME, knh_Token_tochar(StmtDECL_name(stmt)));
			knh_Stmt_done(ctx, stmt);
			return;
		}
		if(FIELDN_IS_U1(fnq)) {
			knh_Compiler_perror(ctx, cpr, KMSG_IGFIELDNAME, knh_Token_tochar(StmtDECL_name(stmt)));
			knh_Stmt_done(ctx, stmt);
			return;
		}
		if(knh_Term_isASIS(StmtDECL_value(stmt))) {
			Object *value = KNH_NULL;
			if(TYPE_ISNOTNULL(type)) {
				value = knh_tClass_defaultValue(ctx, var_cid);
			}
			knh_Compiler_declareVariable(ctx, cpr, flag, type, fn, value);
			knh_Stmt_done(ctx, stmt);
		}
		else {
			knh_Compiler_declareVariable(ctx, cpr, flag, type, fn, KNH_NULL);
			knh_StmtDECL_toLET(ctx, stmt);
		}
	}
}

/* ======================================================================== */
/* [LET] */

#ifndef StmtMACRO
#define StmtLET_lvalue(stmt)        DP(stmt)->tokens[0]
#define StmtLET_rvalue(stmt)        DP(stmt)->terms[1]
#endif

void knh_StmtLET_name(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, int level)
{
	if(DP(StmtLET_lvalue(stmt))->tt == TT_CONSTN) {
		if(level > 1) {
			knh_Compiler_perror(ctx, cpr, KMSG_NOTHERECONST, NULL);
			knh_Stmt_done(ctx, stmt);
			return ;
		}
		
		knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, CLASS_Any);
		if(!knh_Term_isCONST(StmtLET_rvalue(stmt))) {
			knh_Compiler_perror(ctx, cpr, KMSG_NNCONST, NULL);
			knh_Stmt_done(ctx, stmt);
			return ;
		}
		Object *value = knh_Term_constValue(ctx, StmtLET_rvalue(stmt), CLASS_Any);

		if(level == 0) {
			knh_bytes_t cn = knh_Token_tobytes(StmtLET_lvalue(stmt));
			knh_index_t idx = knh_bytes_index(cn, '.');
			if(idx == -1) {
				String *s = (String*)DP(StmtLET_lvalue(stmt))->data;
				DEBUG3_ASSERT(IS_bString(s));
				knh_NameSpace_addLocalConst(ctx, ns, s, value);
			}
			else {
				knh_bytes_t fn = knh_bytes_first(cn, idx);
				knh_class_t cid = knh_NameSpace_getClass(ctx, ns, fn);
				if(cid == CLASS_unknown) {
					knh_Token_perror(ctx, StmtLET_lvalue(stmt), KMSG_UCLASSN);
					knh_Stmt_done(ctx, stmt);
					return ;
				}
				if(!knh_tClass_addClassConst(ctx, cid, knh_bytes_last(cn, idx+1), value)) {
					knh_Token_perror(ctx, StmtLET_lvalue(stmt), KMSG_DUPCONST);
				}
			}
		}
		else if(level == 1) {
			knh_class_t cid = DP(cpr)->this_cid;
			knh_bytes_t cn = knh_Token_tobytes(StmtLET_lvalue(stmt));
			knh_index_t idx = knh_bytes_index(cn, '.');
			if(idx != -1) {
				knh_Compiler_perror(ctx, cpr, KMSG_NOTHERECLASSCONST, NULL);
				knh_Stmt_done(ctx, stmt);
				return ;
			}
			if(!knh_tClass_addClassConst(ctx, cid, knh_bytes_last(cn, idx+1), value)) {
				knh_Token_perror(ctx, StmtLET_lvalue(stmt), KMSG_DUPCONST);
			}
		}
		knh_Stmt_done(ctx, stmt);
		return ;
	} /* CONSTN */
	
	if(level == 1) {
		knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, CLASS_Any);
		if(knh_Term_isCONST(StmtLET_rvalue(stmt))) {
			knh_flag_t  flag = KNH_FLAG_CFF_AUTONAME;
			knh_fieldn_t fnq = knh_Token_tofnq(ctx, StmtLET_lvalue(stmt));
			knh_fieldn_t fn = FIELDN_UNMASK(fnq);
			Object *value = knh_Term_constValue(ctx, StmtLET_rvalue(stmt), CLASS_Any);
			knh_type_t type = knh_Term_get_type(StmtLET_rvalue(stmt));
			if(type == CLASS_Nue) type = CLASS_Any;
			knh_notice_TypeInf(ctx, DP(stmt)->fileid, DP(stmt)->line, FIELDN(fn), type);
			if(FIELDN_IS_U1(fnq) || FIELDN_IS_U2(fnq)) {
				flag |= KNH_FLAG_CFF_PROTECTED;
			}
			knh_Compiler_declareVariable(ctx, cpr, flag, type, fn, value);
		}
		else {
			knh_Compiler_perror(ctx, cpr, KMSG_NNCONST, NULL);
			knh_Stmt_done(ctx, stmt);
		}
		return ;
	}
	else {
		knh_flag_t  flag = KNH_FLAG_CFF_AUTONAME;
		knh_fieldn_t fnq = knh_Token_tofnq(ctx, StmtLET_lvalue(stmt));
		knh_fieldn_t fn = FIELDN_UNMASK(fnq);
		knh_type_t   type = TYPE_Any;
		Object *value = KNH_NULL;
		
		if(level == 0) {
			if(knh_Compiler_indexOfScriptVariable(cpr, fnq) != -1) {
				return ;
			}
			flag |= KNH_FLAG_CFF_GETTER | KNH_FLAG_CFF_SETTER;
			if(FIELDN_IS_U2(fnq)) {
				flag |= KNH_FLAG_CFF_PROTECTED;
			}
			{
				Script *scr = knh_Compiler_getScript(ctx, cpr);
				DP(cpr)->this_cid = knh_Object_cid(scr);
				knh_Compiler_initDecl(ctx, cpr);
				knh_Compiler_initThisVariable(ctx, cpr, DP(cpr)->this_cid);
			}
		}
		else if(level > 1) {
			if(FIELDN_IS_U1(fnq) || FIELDN_IS_U2(fnq)) {
				return;
			}
			if(knh_Compiler_existsName(ctx, cpr, fnq)) {
				return;
			}
		}
	
		knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, CLASS_Any);
		type = knh_Term_get_type(StmtLET_rvalue(stmt));
		if(type == CLASS_Nue) type = CLASS_Any;
		knh_notice_TypeInf(ctx, DP(stmt)->fileid, DP(stmt)->line, FIELDN(fn), type);

		if(knh_Term_isCONST(StmtLET_rvalue(stmt))) {
			value = knh_Term_constValue(ctx, StmtLET_rvalue(stmt), CLASS_Any);
		}

		if(level == 0) {
			knh_Compiler_declareScriptVariable(ctx, cpr, flag, type, fn, value);
		}
		else {
			knh_Compiler_declareVariable(ctx, cpr, flag, type, fn, value);
		}
	}
}

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

static
void KNH_ASM_MOV(Ctx *ctx, Compiler *cpr, Token* n, Term *v)
{
	if(DP(n)->tt == TT_SFPIDX) {
		KNH_ASM_MOVS(ctx, cpr, DP(n)->index, v);
	}
	else if(DP(n)->tt == TT_FLDIDX) {
		KNH_ASM_MOVO(ctx, cpr, DP(n)->index, v);
	}
	else if(DP(n)->tt == TT_OBJIDX) {
		KNH_ASM_MOVOI(ctx, cpr, DP(n)->data, DP(n)->index, v);
	}
	else if(DP(n)->tt == TT_EBPIDX) {
		DEBUG3("EBP[%d]", (int)DP(n)->index);
		KNH_ASM_MOVE(ctx, cpr, DP(n)->index, v);
	}
	else {
		DBG_P("unsupported token tt=%s", knh_token_tochar(DP(n)->tt));
		KNH_ASSERT(ctx == NULL); /* stop here!!*/
	}
}

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

void knh_StmtLET_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	//knh_Stmt_terms_typing(ctx, stmt, 0, cpr, ns, CLASS_Any);
	knh_Stmt_terms_cmpl(ctx, stmt, 0, cpr, ns, CLASS_Any, 0);
	KNH_ASSERT(IS_Token(StmtLET_lvalue(stmt)));
	reqt = DP(StmtLET_lvalue(stmt))->type;
	//knh_Stmt_terms_typing(ctx, stmt, 1, cpr, ns, TYPE_UNMASK_NN(reqt));
	knh_Stmt_terms_cmpl(ctx, stmt, 1, cpr, ns, reqt, 0);
	KNH_ASM_MOV(ctx, cpr, StmtLET_lvalue(stmt), StmtLET_rvalue(stmt));
}


/* ======================================================================== */
/* [letmulti] */

#define StmtLETMULTI_names_size(stmt)   (DP(stmt)->size - 0)
#define StmtLETMULTI_names(stmt,n)      DP(stmt)->tokens[n-0]

void knh_StmtLETMULTI_name(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, int level)
{
	TODO();
}

/* ======================================================================== */
/* [FOREACH] */

knh_fieldn_t knh_tName_getFieldnOnStack(Ctx *ctx, int level)
{
	char bufn[40];
	knh_snprintf(bufn, sizeof(bufn), "__%d__", level);
	knh_bytes_t tname = B(bufn);
	
	knh_index_t idx = knh_DictIdx_index(ctx, DP(knh_rootSystem)->tfieldnDictIdx, tname);
	if(idx == -1) {
		String *s = new_String(ctx, tname, NULL);
		idx = knh_DictIdx_add__fast(ctx, DP(knh_rootSystem)->tfieldnDictIdx, s);
	}
	//DEBUG("level=%d, fn=%d", level, idx);
	return (knh_fieldn_t)idx;
}

/* ------------------------------------------------------------------------ */
#ifndef StmtMACRO
#define StmtFOREACH_type(stmt)          DP(stmt)->tokens[0]
#define StmtFOREACH_name(stmt)          DP(stmt)->tokens[1]
#define StmtFOREACH_iter(stmt)          DP(stmt)->terms[2]
#define StmtFOREACH_where(stmt)         DP(stmt)->terms[3]
#define StmtFOREACH_loop(stmt)          DP(stmt)->stmts[4]
#endif

void knh_StmtFOREACH_name(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, int level)
{
	if(level > 1) {
		knh_fieldn_t fn = knh_Token_tofnq(ctx, StmtFOREACH_name(stmt));
		if(DP(StmtFOREACH_type(stmt))->tt == TT_ASIS) {
			knh_index_t idx = knh_Compiler_indexOfVariable(cpr, fn);
			if(idx == -1) {
				DEBUG3("type inferencing..");
				knh_Stmt_terms_typing(ctx, stmt, FOREACH_iter, cpr, ns, CLASS_Any);
				knh_class_t itrcid = knh_Term_get_cid(StmtFOREACH_iter(stmt));
				knh_type_t  type;
				DEBUG_ASSERT_cid(itrcid);
				if(knh_tClass[itrcid].bcid == CLASS_Iterator) {
					type = CLASS_TONNTYPE(knh_tClass[itrcid].p1);
				}
				else {
					Stmt *stmt_call = new_Stmt(ctx, 0, STT_CALL);
					Token *tk = new_TokenMN(ctx, stmt, METHODN_opItr);
					knh_Stmt_add(ctx, stmt_call, tk);
					knh_Stmt_add(ctx, stmt_call, StmtFOREACH_iter(stmt));
					KNH_SETv(ctx, DP(stmt)->stmts[FOREACH_iter], stmt_call);
					
					Method *mtd = knh_Class_getMethod(ctx, itrcid, METHODN_opItr);
					DEBUG3_ASSERT(IS_NOTNULL(mtd));
					itrcid = knh_pmztype_toclass(ctx, knh_Method_rtype(mtd), itrcid);
					DEBUG_ASSERT_cid(itrcid);
					DEBUG3_ASSERT(knh_tClass[itrcid].bcid == CLASS_Iterator);
					type = CLASS_TONNTYPE(knh_tClass[itrcid].p1);
				}
				knh_notice_TypeInf(ctx, DP(stmt)->fileid, DP(stmt)->line, knh_Token_tochar(StmtFOREACH_name(stmt)), type);
				knh_Compiler_declareVariable(ctx, cpr, 0, type, fn, knh_tClass_defaultValue(ctx, TYPE_UNMASK_NN(type)));
			}
			else {
				DEBUG3("type declared ..");
				/* do nothing */
			}
		}
		else {
			DEBUG3("defining type..");
			knh_type_t type = knh_Token_totype(ctx, StmtFOREACH_type(stmt), CLASS_Any, ns);
			type = knh_pmztype_totype(ctx, type, DP(cpr)->this_cid);
			if(TYPE_ISNULLABLE(type)) {
				knh_Compiler_declareVariable(ctx, cpr, 0, type, fn, KNH_NULL);
			}
			else {
				knh_Compiler_declareVariable(ctx, cpr, 0, type, fn, knh_tClass_defaultValue(ctx, TYPE_UNMASK_NN(type)));
			}
		}
		knh_Compiler_declareVariable(ctx, cpr, 0, NNTYPE_Iterator, knh_tName_getFieldnOnStack(ctx, level), KNH_NULL);
		knh_Stmt_name(ctx, StmtFOREACH_loop(stmt), cpr, ns, level+1);
	}
}

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

void knh_StmtFOREACH_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	String *label = knh_Compiler_lstack_label(ctx, cpr);
	KNH_ASSERT(label != NULL);

	char lbend[LABEL_BUFSIZ];
	knh_snprintf(lbend, sizeof(lbend), "_END_%s", knh_String_tochar(label));
	
	knh_Stmt_terms_typing(ctx, stmt, FOREACH_name, cpr, ns, CLASS_Any);
	if(DP(StmtFOREACH_name(stmt))->tt != TT_SFPIDX) {
		//knh_Compiler_perror(ctx, cpr, KMSG_NOTLOCALVAR);
		KNH_ASM_THROW__OBJ(ctx, cpr, (Object*)TS_SYNTAXERR);
		knh_Compiler_setStopped(cpr, 1);
		return;
	}
	int name_sfp = DP(StmtFOREACH_name(stmt))->index;
	knh_type_t vtype = knh_Term_get_type(DP(stmt)->tokens[FOREACH_name]);
	knh_class_t reqc = knh_class_Iterator(ctx, TYPE_UNMASK_NN(vtype));

	knh_Stmt_terms_typing(ctx, stmt, FOREACH_iter, cpr, ns, reqc);
	knh_class_t itrc = knh_Term_get_cid(StmtFOREACH_iter(stmt));
	knh_index_t itr_sfp = knh_Compiler_indexOfVariable(cpr, knh_tName_getFieldnOnStack(ctx, level));
	
	if(knh_class_instanceof(itrc, reqc)) {
		knh_Stmt_terms_cmpl(ctx, stmt, FOREACH_iter, cpr, ns, reqc, 0);
		KNH_ASM_MOVS(ctx, cpr, itr_sfp, StmtFOREACH_iter(stmt));
		KNH_ASM_LABEL__b(ctx, cpr, knh_String_tobytes(label));
		KNH_ASM_NEXT(ctx, cpr, B(lbend), name_sfp, itr_sfp);
	}
	else {
		Mapper *mpr = knh_tmapper_find(ctx, itrc, reqc);
		int eachbyeach = 0;
		if(knh_Mapper_isNoSuchMapping(mpr)) {
			mpr = knh_tmapper_find(ctx, itrc, CLASS_Iterator);
			knh_Compiler_perror(ctx, cpr, KMSG_WMAPFOREACH, NULL);
			eachbyeach = 1;
		}
		knh_Stmt_terms_cmplpush(ctx, stmt, FOREACH_iter, cpr, ns, itrc, 0);
		KNH_ASM_SMAP(ctx, cpr, (Object*)mpr);
		KNH_ASM_MOVS__ebp(ctx, cpr, itr_sfp, 0);
		KNH_ASM_LABEL__b(ctx, cpr, knh_String_tobytes(label));
		if(eachbyeach) {
			DEBUG3("mapping.. each by each");
			KNH_ASM_MAPNEXT(ctx, cpr, B(lbend), name_sfp, itr_sfp, TYPE_UNMASK_NN(vtype));
		}
		else {
			KNH_ASM_NEXT(ctx, cpr, B(lbend), name_sfp, itr_sfp);
		}
	}
	if(!knh_Term_isCONST(StmtFOREACH_where(stmt))) {
		knh_Stmt_terms_cmpl(ctx, stmt, FOREACH_where, cpr, ns, NNTYPE_Boolean, 0);
		KNH_ASM_JMP_IFF__ebp(ctx, cpr, knh_String_tobytes(label), 0);
	}
	knh_Stmt_cmpls(ctx, StmtFOREACH_loop(stmt), cpr, ns, level+1);
	KNH_ASM_JMP(ctx, cpr, knh_String_tobytes(label));
	/* end */
	KNH_ASM_LABEL__b(ctx, cpr, B(lbend));
}

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

#ifdef __cplusplus
}
#endif
