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

/* ======================================================================== */
/* [Label] */

INLINE
static int knh_Compiler_llstep(Compiler *cpr)
{
	return (int)DP(cpr)->llstep++;
}

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

static
void knh_Compiler_setGotoLabel(Ctx *ctx, Compiler *cpr, String *label)
{
	knh_DictIdx_add(ctx, DP(cpr)->labelIdDictIdx, label);
}

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

static
knh_bool_t knh_Compiler_hasGotoLabel(Ctx *ctx, Compiler *cpr, knh_bytes_t label)
{
#if defined(KNH_DBGMODE)
	knh_ushort_t id = (knh_ushort_t)knh_DictIdx_add__b(ctx, DP(cpr)->labelIdDictIdx, label);
	DBG2_P("LABEL %s id=%d", label.buf, (int)id);
#endif
	return (knh_DictIdx_add__b(ctx, DP(cpr)->labelIdDictIdx, label) != 0);
}

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

static
void knh_Compiler_setBreakLabel(Ctx *ctx, Compiler *cpr, String *label)
{
	knh_DictIdx_add(ctx, DP(cpr)->labelIdDictIdx, label);
	{
		char bufl[LABEL_BUFSIZ];
		knh_snprintf(bufl, sizeof(bufl), "_END_%s", knh_String_tochar(label));
		knh_DictIdx_add__b(ctx, DP(cpr)->labelIdDictIdx, B(bufl));
	}
}

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

static
knh_bool_t knh_Compiler_hasBreakLabel(Ctx *ctx, Compiler *cpr, knh_bytes_t label)
{
	char bufl[LABEL_BUFSIZ];
	knh_snprintf(bufl, sizeof(bufl), "_END_%s", label.buf);
	knh_DictIdx_add__b(ctx, DP(cpr)->labelIdDictIdx, B(bufl));
	return knh_Compiler_hasGotoLabel(ctx, cpr, B(bufl));
}

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

knh_short_t knh_Compiler_labelId(Ctx *ctx, Compiler *cpr, knh_bytes_t label)
{
	knh_ushort_t id = (knh_ushort_t)knh_DictIdx_add__b(ctx, DP(cpr)->labelIdDictIdx, label);
//	DEBUG("LABEL %s id=%d", label.buf, (int)id);
	return id;
}


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

static
void KNH_ASM_LABEL__b(Ctx *ctx, Compiler *cpr, knh_bytes_t t)
{
	if(knh_DictSet_get__b(DP(cpr)->labelAddrDictSet, t) != 0) {
		DBG_P("Duplicated label: %s", t.buf);
		KNH_ASSERT(ctx == NULL);
		return;
	}
	knhvmc_t *pc = (knhvmc_t*)knh_Bytes_last(DP(cpr)->elf);
	knh_DictSet_put(ctx, DP(cpr)->labelAddrDictSet, new_String(ctx, t, NULL), (knh_uint_t)pc);
	//DEBUG("SET LABEL '%s' at %p", t.buf, pc);
}

/* ======================================================================== */
/* [lstack] */

static
knh_bool_t knh_Stmt_hasContinueBreak(Stmt *stmt)
{
	switch(DP(stmt)->stt) {
		case STT_WHILE:
		case STT_DO:
		case STT_FOR:
		case STT_FOREACH:	
#ifdef STT_SWITCH
		case STT_SWITCH:
#endif
			return 1;
	}
	return 0;
}

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

static
void knh_Compiler_lstack_push(Ctx *ctx, Compiler *cpr, String *label)
{
	DEBUG3("label=%s", knh_String_tochar(label));
	knh_Array_add(ctx, DP(cpr)->lstacks, label);
}

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

static
String *knh_Compiler_lstack_label(Ctx *ctx, Compiler *cpr)
{
	size_t s = knh_Array_size(DP(cpr)->lstacks);
	if(s == 0) {
		return NULL;
	}
	return (String*)knh_Array_n(DP(cpr)->lstacks, s-1);
}

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

static
void knh_Compiler_lstack_pop(Ctx *ctx, Compiler *cpr)
{
	knh_Array_pop(ctx, DP(cpr)->lstacks);
}

/* ======================================================================== */
/* [label] */

static
String *knh_StmtMETA_getLabel(Ctx *ctx, Stmt *o)
{
	if(IS_DictMap(DP(o)->metaDictMap)) {
		return (String*)knh_DictMap_get(ctx, DP(o)->metaDictMap, TS_ATlabel);
	}
	return KNH_NULL;
}

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

void
knh_Stmt_cmpl_beforeLABEL(Ctx *ctx, Stmt *stmt, Compiler *cpr, int level)
{
	if(knh_Stmt_hasContinueBreak(stmt)) {
		String *label = knh_StmtMETA_getLabel(ctx, stmt);
		if(IS_NULL(label)) {
			char lb[LABEL_BUFSIZ];
			knh_snprintf(lb, sizeof(lb), "_L%d_", knh_Compiler_llstep(cpr));
			label = new_String(ctx, B(lb), NULL);
		}
		knh_Compiler_lstack_push(ctx, cpr, label);
		knh_Compiler_setBreakLabel(ctx, cpr, label);
	}
	else {
		String *label = knh_StmtMETA_getLabel(ctx, stmt);
		if(IS_NOTNULL(label) && DP(stmt)->stt != STT_DONE) {
			knh_Compiler_setGotoLabel(ctx, cpr, label);
			KNH_ASM_LABEL__b(ctx, cpr, knh_String_tobytes(label));
			DEBUG3("GOTO_LABEL: %s", knh_String_tochar(label));
		}
	}
}

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

void knh_Stmt_cmpl_afterLABEL(Ctx *ctx, Stmt *stmt, Compiler *cpr, int level)
{
	/* break-labeling */
	if(knh_Stmt_hasContinueBreak(stmt)) {
		DBG_({
		String *label = knh_Compiler_lstack_label(ctx, cpr);
		KNH_ASSERT(label != NULL);
		})
		knh_Compiler_lstack_pop(ctx, cpr);
	}
}

/* ======================================================================== */
/* [IF] */
#ifndef StmtMACRO
#define StmtIF_bool(stmt)               DP(stmt)->terms[0]
#define StmtIF_truecase(stmt)           DP(stmt)->stmts[1]
#define StmtIF_falsecase(stmt)          DP(stmt)->stmts[2]
#endif

void knh_StmtIF_name(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns,int level)
{
	if(level > 1) {
		knh_Stmt_names(ctx, StmtIF_truecase(stmt), cpr, ns, level + 1);
		knh_Stmt_names(ctx, StmtIF_falsecase(stmt), cpr, ns, level + 1);
	}
}

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

void knh_StmtIF_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	knh_Stmt_terms_cmpl(ctx, stmt, 0, cpr, ns, CLASS_Boolean, 0 /*MC_FIRST*/);
	
	if(knh_Term_isCONST(StmtIF_bool(stmt))) {
		if(IS_TRUE(knh_Term_constValue(ctx, StmtIF_bool(stmt), CLASS_Any))) {
			knh_Stmt_cmpls(ctx, StmtIF_truecase(stmt), cpr, ns, level + 1);
		}
		else {
			knh_Stmt_cmpls(ctx, StmtIF_falsecase(stmt), cpr, ns, level + 1);
		}
		return; 
	}

	{
		char lbelse[LABEL_BUFSIZ];
		char lbend[LABEL_BUFSIZ];
		knh_snprintf(lbelse, sizeof(lbelse), "_ELSE_%d", knh_Compiler_llstep(cpr));
		knh_snprintf(lbend, sizeof(lbend), "_END_%d", knh_Compiler_llstep(cpr));
		
		/* if */
		if(knh_Term_get_type(StmtIF_bool(stmt)) == NNTYPE_Boolean) {
			KNH_ASM_JMP_IFF(ctx, cpr, B(lbelse), StmtIF_bool(stmt));
		}
		else {
			KNH_ASM_JMP_IFFN(ctx, cpr, B(lbelse), StmtIF_bool(stmt));
		}
		knh_Stmt_cmpls(ctx, StmtIF_truecase(stmt), cpr, ns, level + 1);
		KNH_ASM_JMP(ctx, cpr, B(lbend));
	
		/* else */
		KNH_ASM_LABEL__b(ctx, cpr, B(lbelse));
		knh_Stmt_cmpls(ctx, StmtIF_falsecase(stmt), cpr, ns, level + 1);
		/* endif */
		KNH_ASM_LABEL__b(ctx, cpr, B(lbend));
	}
}

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

void knh_StmtALT_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	char bufl[32];
	knh_snprintf(bufl, sizeof(bufl), "LL%3d", knh_Compiler_llstep(cpr));
	{
		knh_bytes_t lbn = B(bufl);
		int i, needs_nullcheck = 0;
		for(i = 0; i < DP(stmt)->size; i++) {
			knh_Stmt_terms_cmplpush(ctx, stmt, i, cpr, ns, reqt, level);

			level = 0; /* isfirst = 1*/
			if(i + 1 < DP(stmt)->size) {
				KNH_ASM_JMP_IFNN__ebp(ctx, cpr, lbn, 0);
			}
		}
		DP(stmt)->type = reqt;
		for(i = 0; i < DP(stmt)->size; i++) {
			knh_type_t type = knh_Term_type(DP(stmt)->terms[i]);
			if(type == reqt) continue;
			if(TYPE_ISNULLABLE(type)) {
				DEBUG3("needs nullcheck %s%s", TYPEQN(type));
				needs_nullcheck = 1;
			}
			DP(stmt)->type = TYPE_TONNTYPE(CLASS_Any);
		}
		if(needs_nullcheck) {
			KNH_ASM_NULLCHK__ebp(ctx, cpr, 0);
		}
		KNH_ASM_LABEL__b(ctx, cpr, lbn);
	}
	knh_Stmt_done(ctx, stmt);
}


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


void knh_StmtAND_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	char bufl[32];
	knh_snprintf(bufl, sizeof(bufl), "LL%3d", knh_Compiler_llstep(cpr));
	{
		knh_type_t  rtype = NNTYPE_Boolean;
		knh_bytes_t lbn = B(bufl);
		int i;
		for(i = 0; i < DP(stmt)->size; i++) {
			knh_Stmt_terms_cmplpush(ctx, stmt, i, cpr, ns, CLASS_Any, level);
			level = 0; /* isfirst = 1*/
			if(i + 1 < DP(stmt)->size) {
				if(knh_Term_get_type(DP(stmt)->terms[i]) == NNTYPE_Boolean) {
					KNH_ASM_JMP_IFF__ebp(ctx, cpr, lbn, 0);
				}
				else {
					KNH_ASM_JMP_IFFN__ebp(ctx, cpr, lbn, 0);
					rtype = CLASS_Any;
				}
			}
		}
		KNH_ASM_LABEL__b(ctx, cpr, lbn);
		DP(stmt)->type = rtype;
	}
}

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

void knh_StmtOR_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	char bufl[32];
	knh_snprintf(bufl, sizeof(bufl), "LL%3d", knh_Compiler_llstep(cpr));
	{
		knh_bytes_t lbn = B(bufl);
		knh_type_t  rtype = NNTYPE_Boolean;
		int i;
		for(i = 0; i < DP(stmt)->size; i++) {
			knh_Stmt_terms_cmplpush(ctx, stmt, i, cpr, ns, TYPE_Boolean, level);
			level = 0; /* isfirst = 1*/
			if(i + 1 < DP(stmt)->size) {
				if(knh_Term_get_type(DP(stmt)->terms[i]) == NNTYPE_Boolean) {
					KNH_ASM_JMP_IFT__ebp(ctx, cpr, lbn, 0);
				}
				else {
					KNH_ASM_JMP_IFTNN__ebp(ctx, cpr, lbn, 0);
					rtype = CLASS_Any;
				}
			}
		}
		KNH_ASM_LABEL__b(ctx, cpr, lbn);
		DP(stmt)->type = rtype;
	}
}

/* ------------------------------------------------------------------------ */
#ifndef StmtMACRO
#define StmtTRINARY_bool(stmt)          DP(stmt)->terms[0]
#define StmtTRINARY_tvalue(stmt)        DP(stmt)->terms[1]
#define StmtTRINARY_fvalue(stmt)        DP(stmt)->terms[2]
#endif

void knh_StmtTRINARY_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	char lbelse[LABEL_BUFSIZ];
	char lbend[LABEL_BUFSIZ];
	knh_snprintf(lbelse, sizeof(lbelse), "ELSE_LL%d", knh_Compiler_llstep(cpr));
	knh_snprintf(lbend, sizeof(lbend), "END_LL%d", knh_Compiler_llstep(cpr));

	knh_Stmt_terms_cmpl(ctx, stmt, 0, cpr, ns, TYPE_Boolean, level);
	
	/* if */
	if(knh_Term_get_type(StmtTRINARY_bool(stmt)) == NNTYPE_Boolean) {
		KNH_ASM_JMP_IFF(ctx, cpr, B(lbelse), StmtTRINARY_bool(stmt));
	}
	else {
		KNH_ASM_JMP_IFFN(ctx, cpr, B(lbend), StmtTRINARY_bool(stmt));
	}
	knh_Stmt_terms_cmplpush(ctx, stmt, 1, cpr, ns, reqt, 0 /* MC_FIRST */);
	KNH_ASM_JMP(ctx, cpr, B(lbend));

	/* else */
	KNH_ASM_LABEL__b(ctx, cpr, B(lbelse));
	knh_Stmt_terms_cmplpush(ctx, stmt, 2, cpr, ns, reqt, 0 /* MC_FIRST */);
	KNH_ASM_LABEL__b(ctx, cpr, B(lbend));
	{
		knh_type_t ttype = knh_Term_type(StmtTRINARY_tvalue(stmt));
		knh_type_t ftype = knh_Term_type(StmtTRINARY_tvalue(stmt));
		DEBUG3("ttype=%s%s, ftype=%s%s", TYPEQN(ttype), TYPEQN(ftype));
		if(ttype == ftype) {
			if(ttype == reqt) {
				DP(stmt)->type = reqt;
			}
			else {
				DP(stmt)->type = ttype;
			}
		}
		else {
			DP(stmt)->type = CLASS_Any;
			//DP(stmt)->type = knh_type_parentType(ttype, ftype);
		}
	}
	knh_Stmt_done(ctx, stmt);
}

/* ======================================================================== */
/* [WHILE] */
#ifndef StmtMACRO
#define StmtWHILE_bool(stmt)            DP(stmt)->terms[0]
#define StmtWHILE_loop(stmt)            DP(stmt)->stmts[1]
#endif

void knh_StmtWHILE_name(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, int level)
{
	if(level > 1) {
		knh_Stmt_names(ctx, StmtWHILE_loop(stmt), cpr, ns, level + 1);
	}
}

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

void knh_StmtWHILE_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	String *label = knh_Compiler_lstack_label(ctx, cpr);
	char lbend[LABEL_BUFSIZ];
	KNH_ASSERT(label != NULL);
	//DEBUG3("while label='%s'", knh_String_tochar(label));
	knh_snprintf(lbend, sizeof(lbend), "_END_%s", knh_String_tochar(label));
	
	KNH_ASM_LABEL__b(ctx, cpr, knh_String_tobytes(label));
	knh_Stmt_terms_cmpl(ctx, stmt, 0, cpr, ns, CLASS_Boolean, 0 /* MC_FIRST */);
	
	if(knh_Term_isCONST(StmtWHILE_bool(stmt))) {
		if(IS_TRUE(knh_Term_constValue(ctx, StmtWHILE_bool(stmt), CLASS_Any))) { /* infinite loop */
			knh_Stmt_cmpls(ctx, StmtWHILE_loop(stmt), cpr, ns, level + 1);
			KNH_ASM_JMP(ctx, cpr, knh_String_tobytes(label));
		}
		else {
			KNH_ASM_JMP(ctx, cpr, B(lbend));
		}
	}
	else { /* whilc(bool) .. */
		if(knh_Term_get_type(StmtWHILE_bool(stmt)) == NNTYPE_Boolean) {
			KNH_ASM_JMP_IFF(ctx, cpr, B(lbend), StmtWHILE_bool(stmt));
		}
		else {
			KNH_ASM_JMP_IFFN(ctx, cpr, B(lbend), StmtWHILE_bool(stmt));
		}
		knh_Stmt_cmpls(ctx, StmtWHILE_loop(stmt), cpr, ns, level + 1);
		KNH_ASM_JMP(ctx, cpr, knh_String_tobytes(label));
	}
	/* end */
	KNH_ASM_LABEL__b(ctx, cpr, B(lbend));
}

/* ------------------------------------------------------------------------ */
/* [DO] */

#ifndef StmtMACRO
#define StmtDO_loop(stmt)               DP(stmt)->stmts[0]
#define StmtDO_bool(stmt)               DP(stmt)->terms[1]
#endif

void knh_StmtDO_name(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, int level)
{
	if(level > 1) {
		knh_Stmt_names(ctx, StmtDO_loop(stmt), cpr, ns, level + 1);
	}
}

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

void knh_StmtDO_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	String *label = knh_Compiler_lstack_label(ctx, cpr);
	char lbend[LABEL_BUFSIZ];
	KNH_ASSERT(label != NULL);
	knh_snprintf(lbend, sizeof(lbend), "_END_%s", knh_String_tochar(label));

	KNH_ASM_LABEL__b(ctx, cpr, knh_String_tobytes(label));

	/* loop */
	knh_Stmt_cmpls(ctx, StmtDO_loop(stmt), cpr, ns, level + 1);

	knh_Stmt_terms_cmpl(ctx, stmt, 1, cpr, ns, CLASS_Boolean, 0 /* MC_FIRST */);
	if(knh_Term_isCONST(StmtDO_bool(stmt))) {
		if(IS_TRUE(knh_Term_constValue(ctx, StmtDO_bool(stmt), CLASS_Any))) { /* infinite loop */
			KNH_ASM_JMP(ctx, cpr, knh_String_tobytes(label));
		}
		else {
		}
	}
	else {
		if(knh_Term_get_type(StmtDO_bool(stmt)) == NNTYPE_Boolean) {
			KNH_ASM_JMP_IFT(ctx, cpr, knh_String_tobytes(label), StmtDO_bool(stmt));
		}
		else {
			KNH_ASM_JMP_IFTNN(ctx, cpr, knh_String_tobytes(label), StmtDO_bool(stmt));
		}
	}
	/* end */
	KNH_ASM_LABEL__b(ctx, cpr, B(lbend));
}

/* ------------------------------------------------------------------------ */
/* [LABEL UTIL]*/

static 
String *knh_Term_getLabel(Term *tm, String *label)
{
	Token *tk = (Token*)tm;
	KNH_ASSERT(IS_Token(tm));
	if(IS_String(DP(tk)->data)) {
		return (String*)DP(tk)->data;
	}
	DEBUG3("token tt=%s", knh_token_tochar(DP(tk)->tt));
	return label;
}

/* ------------------------------------------------------------------------ */
/* [CONTINUE] */
#ifndef StmtMACRO
#define StmtCONTINUE_label(stmt)         DP(stmt)->terms[0]
#endif

void knh_StmtCONTINUE_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	String *label = knh_Compiler_lstack_label(ctx, cpr);
	if(label == NULL) {
		knh_Compiler_perror(ctx, cpr, KMSG_EOUTERLOOP, "continue");
		knh_Stmt_done(ctx, stmt);
		return;
	}
	if(DP(stmt)->size == 1) {
		label = knh_Term_getLabel(StmtCONTINUE_label(stmt), label);
		DEBUG3("continue %s", knh_String_tochar(label));
	}

	if(knh_Compiler_hasBreakLabel(ctx, cpr, knh_String_tobytes(label))) {
		KNH_ASM_JMP(ctx, cpr, knh_String_tobytes(label));
	}
	else {
		knh_Compiler_perror(ctx, cpr, KMSG_ULABEL, knh_String_tochar(label));
	}
	knh_Stmt_done(ctx, stmt);
}

/* ------------------------------------------------------------------------ */
/* [BREAK] */
#ifndef StmtMACRO
#define StmtBREAK_label(stmt)         DP(stmt)->terms[0]
#endif

void knh_StmtBREAK_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	String *label = knh_Compiler_lstack_label(ctx, cpr);
	if(label == NULL) {
		knh_Compiler_perror(ctx, cpr, KMSG_EOUTERLOOP, "break");
		knh_Stmt_done(ctx, stmt);
		return;
	}
	if(DP(stmt)->size == 1) {
		label = knh_Term_getLabel(StmtBREAK_label(stmt), label);
		DEBUG3("break %s", knh_String_tochar(label));
	}
	if(knh_Compiler_hasBreakLabel(ctx, cpr, knh_String_tobytes(label))) {
		char lb[LABEL_BUFSIZ];
		knh_snprintf(lb, sizeof(lb), "_END_%s", knh_String_tochar(label));
		KNH_ASM_JMP(ctx, cpr, B(lb));
	}
	else {
		knh_Compiler_perror(ctx, cpr, KMSG_ULABEL, knh_String_tochar(label));
	}
	knh_Stmt_done(ctx, stmt);
}

/* ------------------------------------------------------------------------ */
/* [GOTO] */
#ifndef StmtMACRO
#define StmtGOTO_label(stmt)            DP(stmt)->tokens[0]
#endif

void knh_StmtGOTO_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	if(knh_StmtMETA_isHistoric(stmt)) {
		String *label = knh_Term_getLabel(StmtGOTO_label(stmt), TS_EMPTY);
		if(knh_Compiler_hasGotoLabel(ctx, cpr, knh_String_tobytes(label))) {
			knh_Compiler_perror(ctx, cpr, KMSG_ULABEL, knh_String_tochar(label));
			knh_Compiler_stopCompilation(ctx, cpr);
		}
		else {
			KNH_ASM_JMP(ctx, cpr, knh_String_tobytes(label));
		}
	}
	else {
		knh_Compiler_perror(ctx, cpr, KMSG_AHISTORIC, "goto");
	}
	knh_Stmt_done(ctx, stmt);
}

/* ------------------------------------------------------------------------ */
#ifndef StmtMACRO
#define StmtFOR_init(stmt)              DP(stmt)->terms[0]
#define StmtFOR_bool(stmt)              DP(stmt)->terms[1]
#define StmtFOR_redo(stmt)              DP(stmt)->terms[2]
#define StmtFOR_loop(stmt)              DP(stmt)->stmts[3]
#endif

void knh_StmtFOR_name(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, int level)
{
	if(level > 1) {
		if(IS_Stmt(DP(stmt)->terms[0])) {
			knh_Stmt_name(ctx, DP(stmt)->stmts[0], cpr, ns, level+1);
		}
		knh_Stmt_name(ctx, DP(stmt)->stmts[3], cpr, ns, level+1);
	}
}

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

void knh_StmtFOR_cmpl(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
{
	char lbredo[LABEL_BUFSIZ];
	char lbend[LABEL_BUFSIZ];
	String *label = knh_Compiler_lstack_label(ctx, cpr);
	KNH_ASSERT(label != NULL);
	DEBUG3("for label='%s'", knh_String_tochar(label));
	//KNH_ASM_LABEL__b(ctx, cpr, knh_String_tobytes(label));

	/* i = 1 part */
	knh_Stmt_cmpl(ctx, DP(stmt)->stmts[0], cpr, ns, TYPE_Any, level+1);
	
	knh_snprintf(lbredo, sizeof(lbredo), "_REDO_%s", knh_String_tochar(label));
	knh_snprintf(lbend, sizeof(lbend), "_END_%s", knh_String_tochar(label));
	KNH_ASM_JMP(ctx, cpr, B(lbredo)); /* GOTO REDO */

	/* i++ part */
	KNH_ASM_LABEL__b(ctx, cpr, knh_String_tobytes(label)); /* CONTINUE */
	knh_Stmt_cmpl(ctx, DP(stmt)->stmts[2], cpr, ns, TYPE_Any, level+1);

	/* i < 10 part */
	KNH_ASM_LABEL__b(ctx, cpr, B(lbredo)); /* REDO */

	knh_Stmt_terms_cmpl(ctx, stmt, 1, cpr, ns, CLASS_Boolean, 0 /* MC_FIRST */);
	
	if(knh_Term_isCONST(StmtFOR_bool(stmt))) {
		if(IS_TRUE(knh_Term_constValue(ctx, StmtFOR_bool(stmt), CLASS_Any))) { /* infinite loop */
			knh_Stmt_cmpls(ctx, StmtFOR_loop(stmt), cpr, ns, level + 1);
			KNH_ASM_JMP(ctx, cpr, knh_String_tobytes(label));  /* GOTO CONTINUE */
		}
		else {
			KNH_ASM_JMP(ctx, cpr, B(lbend));
		}
	}
	else { /* whilc(bool) .. */
		if(knh_Term_get_type(StmtFOR_bool(stmt)) == NNTYPE_Boolean) {
			KNH_ASM_JMP_IFF(ctx, cpr, B(lbend), StmtFOR_bool(stmt));
		}
		else {
			KNH_ASM_JMP_IFFN(ctx, cpr, B(lbend), StmtFOR_bool(stmt));
		}
		knh_Stmt_cmpls(ctx, StmtFOR_loop(stmt), cpr, ns, level + 1);
		KNH_ASM_JMP(ctx, cpr, knh_String_tobytes(label));
	}
	/* end */
	KNH_ASM_LABEL__b(ctx, cpr, B(lbend));
}

///* ------------------------------------------------------------------------ */
//
//void knh_Stmtdeclv__foreach(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
//{
//	//DEBUG_STMT(ctx, stmt);
//	/* ['foreach', 'decl', 'FIELDN', 'expr', 'expr', '{'] */
//	if(level > 1) {
//		knh_Stmt_visit(ctx, DP(stmt)->terms[1], knh_Stmtdeclv, v, level+1);
//		knh_Stmt_visit(ctx, DP(stmt)->terms[5], knh_Stmtdeclv, v, level+1);
//		Compiler *b = cpr;
//		knh_Compiler_declareVariable(ctx, cpr, (Token*)DP(stmt)->terms[0], 0, CLASS_any, knh_tName_getFieldnOnStack(ctx, level), KNH_NULL);
//	}
//}
//
///* ------------------------------------------------------------------------ */
//
//void knh_Stmtasm__foreach(Ctx *ctx, Stmt *stmt, Compiler *cpr, NameSpace *ns, knh_type_t reqt, int level)
//{
//	//DEBUG_STMT(ctx, stmt);
//	Compiler *b = cpr;
//	NameSpace *ns = knh_Compiler_getNameSpace(ctx, b);
//
//	String *label = knh_Compiler_lstack_label(ctx, b);
//	KNH_ASSERT(label != NULL);
//
//	/* ['foreach', 'decl', 'FIELDN', 'expr', 'expr', '{'] */
//	
//	knh_Stmt_terms_seval(ctx, stmt, 2, CLASS_any, ns);
//	knh_asmv_t name = knh_Compiler_asmv(ctx, cpr, DP(stmt)->terms[2], TYPE_any, MC_FIRST);
//	if(name.potype != KNH_ASMV_SFP) {
//		MC_EPRINT(ctx, (Token*)DP(stmt)->terms[2], MC_ERROR, "local variable only");
//		knh_Stmt_done(ctx, stmt);
//		return;	
//	}
//
//	knh_index_t itr_sfp = knh_Compiler_indexOfVariable(b, knh_tName_getFieldnOnStack(ctx, level));
//	KNH_ASSERT(itr_sfp != -1);
//	
//	knh_Stmt_terms_seval(ctx, stmt, 3, CLASS_any, ns);
//	knh_asmv_t iter = knh_Compiler_asmv(ctx, cpr, DP(stmt)->terms[3], TYPE_any, MC_FIRST);
//	KNH_ASM_MOVS(ctx, cpr, itr_sfp, iter);
//	KNH_ASM_ITER(ctx, cpr, name.type, itr_sfp);
//
//	KNH_ASM_LABEL__b(ctx, cpr, knh_String_tobytes(label));
//	char lbend[LABEL_BUFSIZ];
//	knh_snprintf(lbend, sizeof(lbend), "_END_%s", knh_String_tochar(label));
//
//	KNH_ASM_FOREACH(ctx, cpr, B(lbend), TYPE_TONOTNULL(name.type), name.index, itr_sfp);
//	knh_Stmt_visit(ctx, DP(stmt)->terms[5], knh_Stmtasm, v, level+1);
//	KNH_ASM_JMP(ctx, cpr, knh_String_tobytes(label));
//
//	/* end */
//	KNH_ASM_LABEL__b(ctx, cpr, B(lbend));
//
//	knh_Stmt_done(ctx, stmt);
//}


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

#ifdef __cplusplus
}
#endif
