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

/* ======================================================================== */
/* [Token] */

knh_bool_t knh_Token_isVARN(Token *tk)
{
	if(SP(tk)->tt == TT_NAME) {
		int i;
		knh_bytes_t t = knh_Token_tobytes(tk);
		for(i = 0; i < t.len; i++) {
			if(t.buf[i] == '.' || t.buf[i] == ':') return 0;
		}
		return 1;
	}
	return 0;
}

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

knh_bool_t knh_Token_isNSNAME(Token *tk)
{
	if(SP(tk)->tt == TT_NAME) {
		int i;
		knh_bytes_t t = knh_Token_tobytes(tk);
		for(i = 0; i < t.len; i++) {
			if(islower(t.buf[i]) || isdigit(t.buf[i]) || t.buf[i] == '.') {
				continue;
			}
			return 0;
		}
		return 1;
	}
	return 0;
}


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

knh_bool_t knh_Token_isNSCLASSN(Token *tk)
{
	if(SP(tk)->tt == TT_NAME) {
		int i, prev = 0;
		knh_bytes_t t = knh_Token_tobytes(tk);
		for(i = 0; i < t.len; i++) {
			if(isupper(t.buf[i])) {
				if(prev == '.') return 1; else return 0;
			}
			if(islower(t.buf[i]) || isdigit(t.buf[i]) || t.buf[i] == '.') {
				prev = t.buf[i];
				continue;
			}
			return 0;
		}
		return 1;
	}
	return 0;
}

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

knh_bool_t knh_Token_isCLASSN(Token *tk)
{
	if(SP(tk)->tt == TT_TYPEN) {
		knh_bytes_t t = knh_Token_tobytes(tk);
		if(isupper(t.buf[0])) {
			int i;
			for(i = 0; i < t.len; i++) {
				if(t.buf[i] == ':') return 0;
			}
		}
		return 1;
	}
	return 0;
}

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

knh_bool_t knh_Token_isMETHODN(Token *tk)
{
	if(SP(tk)->tt == TT_NAME) {
		knh_bytes_t t = knh_Token_tobytes(tk);
		if(isupper(t.buf[0])) {
			int i;
			for(i = 0; i < t.len; i++) {
				if(t.buf[i] == '_' || t.buf[i] == '.') return 0;
			}
		}
		return 1;
	}
	return 0;
}

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

knh_bool_t knh_Token_isCLASSTN(Token *tk)
{
	if(SP(tk)->tt == TT_TYPEN) {
		knh_bytes_t t = knh_Token_tobytes(tk);
		return isupper(t.buf[0]);
	}
	return 0;
}

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

knh_bool_t knh_Token_isCURN(Token *tk)
{
	if(SP(tk)->tt != TT_URN) {
		return knh_Token_isNSCLASSN(tk);
	}
	return 1;
}

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

knh_bool_t knh_Token_isEXPTN(Token *tk)
{
	return (SP(tk)->tt == TT_TYPEN && knh_Token_isExceptionType(tk));
}

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

knh_bool_t knh_Token_isFURN(Token *tk)
{
	switch(SP(tk)->tt) {
		case TT_STR: case TT_TSTR:
		case TT_URN:
		{
			String *s = (String*)DP(tk)->data;
			if(knh_String_endsWith(s, STEXT(".k"))) {
				return 1;
			}
			return 0;
		}
	}
	return 0;
}

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

knh_bool_t knh_Token_isANY(Token *tk)
{
	switch(SP(tk)->tt) {
		case TT_STR: case TT_TSTR:
		case TT_URN:
		case TT_NAME:
			return 1;
		default :
			return 0;
	}
}

/* ======================================================================== */
/* [Stmt_add] */

void
knh_Stmt_add_SEMICOLON(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	if(SP(o)->stt == STT_ERR) return;
	if(tc->c < tc->e && SP(tc->ts[tc->c])->tt == TT_SEMICOLON) {
		tc->c += 1;
	}
	else {
		//DEBUG3("tc->c=%d tc->e=%d tt=%s", tc->c, tc->e, knh_token_tochar(SP(tc->ts[tc->c])->tt));
		knh_perror(ctx, SP(o)->fileid, SP(o)->line, KMSG_WSEMICOLON, NULL);
	}
}

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

void knh_Stmt_add_ASIS(Ctx *ctx, Stmt *o)
{
	if(SP(o)->stt == STT_ERR) return;
	knh_Stmt_add(ctx, o, TM(new_TokenASIS(ctx, FL(o))));
}

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

void
knh_Stmt_add_STEXT(Ctx *ctx, Stmt *o, knh_tokens_t *tc, knh_bytes_t stext)
{
	if(SP(o)->stt == STT_ERR) return;
	knh_Stmt_add(ctx, o, TM(new_Token__text(ctx, knh_tokens_curToken(tc), stext)));
}

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

void
knh_Stmt_add_S(Ctx *ctx, Stmt *o, knh_token_t tt, String *ts)
{
	if(SP(o)->stt == STT_ERR) return;
	knh_Stmt_add(ctx, o, TM(new_Token__S(ctx, FL(o), tt, ts)));
}

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

void
knh_Stmt_add_WITH(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	if(SP(o)->stt == STT_ERR) return;
	if(tc->c < tc->e && SP(tc->ts[tc->c])->tt == TT_WITH) {
		tc->c += 1;
		knh_Stmt_add_EXPR(ctx, o, tc);
	}
	else {
		knh_Stmt_add_ASIS(ctx, o);
	}
	knh_Stmt_add_SEMICOLON(ctx, o, tc);
}

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

void
knh_Stmt_add_PRAGMA(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	if(SP(o)->stt == STT_ERR) return;
//	knh_Stmt_add_VARN(ctx, o, tc);
	if(tc->c < tc->e && SP(tc->ts[tc->c])->tt == TT_LET) {
		tc->c += 1;
		if(tc->c < tc->e) {
			switch(SP(tc->ts[tc->c])->tt) {
				case TT_STR: case TT_NUM: case TT_NAME: case TT_TSTR:
				case TT_TYPEN: case TT_CONSTN: case TT_URN:
					knh_Stmt_add(ctx, o, TM(tc->ts[tc->c]));
					tc->c += 1;
					break;
				default:
					knh_Stmt_perror(ctx, o, tc, KMSG_TARGS);
					return;
			}
		}
		else {
			knh_Stmt_perror(ctx, o, tc, KMSG_TARGS);
		}
	}
	else {
		knh_Stmt_add_ASIS(ctx, o);
	}
	knh_Stmt_add_SEMICOLON(ctx, o, tc);
}

/* ------------------------------------------------------------------------ */
/* [using import] */

static
Stmt *new_StmtUIMPORT(Ctx *ctx, knh_tokens_t *tc)
{
	Stmt *o = new_Stmt(ctx, 0, STT_UIMPORT);
	knh_Stmt_add_NSCLASSN(ctx, o, tc); /* NSCLASSN */
	knh_Stmt_add_SEMICOLON(ctx, o, tc); /* ; */
	return o;
}

/* ------------------------------------------------------------------------ */
/* [using alias] */

//static
//Stmt *new_StmtUALIAS(Ctx *ctx, knh_tokens_t *tc)
//{
//	Stmt *o = new_Stmt(ctx, 0, STT_UALIAS);
//	knh_Stmt_add_CLASSN(ctx, o, tc); /* CLASSN */
//	knh_Stmt_add_CURN(ctx, o, tc); /* CURN */
//	knh_Stmt_add_SEMICOLON(ctx, o, tc); /* ; */
//	return o;
//}

/* ------------------------------------------------------------------------ */
/* [using unit] */

static
Stmt *new_StmtUUNIT(Ctx *ctx, knh_tokens_t *tc)
{
	Stmt *o = new_Stmt(ctx, 0, STT_UUNIT);
	knh_Stmt_add_CLASSTN(ctx, o, tc); /* CLASSTN */
	knh_Stmt_add_CURN(ctx, o, tc); /* CURN */
	knh_Stmt_add_SEMICOLON(ctx, o, tc); /* ; */
	return o;
}

/* ------------------------------------------------------------------------ */
/* [using enum] */

Stmt *new_StmtUENUM(Ctx *ctx, knh_tokens_t *tc)
{
	Stmt *o = new_Stmt(ctx, 0, STT_UENUM);
	knh_Stmt_add_CLASSTN(ctx, o, tc); /* CLASSTN */
	knh_Stmt_add_CURN(ctx, o, tc); /* CURN */
	knh_Stmt_add_SEMICOLON(ctx, o, tc); /* ; */
	return o;
}

/* ------------------------------------------------------------------------ */
/* [using vocab] */

Stmt *new_StmtUVOCAB(Ctx *ctx, knh_tokens_t *tc)
{
	Stmt *o = new_Stmt(ctx, 0, STT_UVOCAB);
	knh_Stmt_add_CLASSTN(ctx, o, tc); /* CLASSTN */
	knh_Stmt_add_CURN(ctx, o, tc); /* CURN */
	knh_Stmt_add_SEMICOLON(ctx, o, tc); /* ; */
	return o;
}

///* ------------------------------------------------------------------------ */
///* [using table] */
//
//Stmt *new_StmtUTABLE(Ctx *ctx, knh_tokens_t *tc)
//{
//	Stmt *o = new_Stmt(ctx, 0, STT_UTABLE);
//	knh_Stmt_add_CLASSTN(ctx, o, tc); /* CLASSTN */
//	knh_Stmt_add_CURN(ctx, o, tc); /* CURN */
//	knh_Stmt_add_WITH(ctx, o, tc); /* with */
//	return o;
//}

/* ------------------------------------------------------------------------ */
/* [CMETHODN] */

#define knh_Token_isCFUNCN(tk)   (knh_Token_isCMETHODN(tk) || (knh_Token_isTYPEN(tk) && knh_Token_isTailWildCard(tk)))

static
void knh_Stmt_add_CFUNCN(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	if(SP(o)->stt == STT_ERR) return;
	if(tc->c < tc->e && knh_Token_isCFUNCN(tc->ts[tc->c])) {
		knh_Stmt_add(ctx, o, UP(tc->ts[tc->c]));
		tc->c += 1;
	}
	else {
		knh_Stmt_perror(ctx, o, tc, KMSG_TCMETHODN);
	}
}

/* ------------------------------------------------------------------------ */
/* [using func] */

Stmt *new_StmtUFUNC(Ctx *ctx, knh_tokens_t *tc)
{
	Stmt *o = new_Stmt(ctx, 0, STT_UFUNC);
	knh_Stmt_add_CFUNCN(ctx, o, tc); /* CMETHODN */
	knh_Stmt_add_SEMICOLON(ctx, o, tc); /* ; */
	return o;
}

/* ------------------------------------------------------------------------ */
/* [using mapmap] */

Stmt *new_StmtUMAPMAP(Ctx *ctx, knh_tokens_t *tc)
{
	Stmt *o = new_Stmt(ctx, 0, STT_UMAPMAP);
	knh_Stmt_add_CLASSTN(ctx, o, tc); /* CLASSTN */
	knh_Stmt_add_CLASSTN(ctx, o, tc); /* CLASSTN */
	knh_Stmt_add_CURN(ctx, o, tc); /* CURN */
	knh_Stmt_add_SEMICOLON(ctx, o, tc); /* ; */
	return o;
}

/* ------------------------------------------------------------------------ */
Stmt *new_StmtUSING(Ctx *ctx, knh_tokens_t *tc)
{
	if(tc->c < tc->e) {
		Stmt *stmt = NULL;
		knh_tokens_t meta_tc = { tc->ts, tc->c, tc->e};
		Token *tk = tc->ts[tc->c];
		knh_bytes_t op = knh_Token_tobytes(tk);

		if(ISB(op, "import")) {
			tc->c += 1;
			stmt = new_StmtUIMPORT(ctx, tc);
			knh_StmtMETA_add_prestmt(ctx, stmt, &meta_tc, -1);
			return stmt;
		}

		if(ISB(op, "unit")) {
			tc->c += 1;
			stmt = new_StmtUUNIT(ctx, tc);
			knh_StmtMETA_add_prestmt(ctx, stmt, &meta_tc, -1);
			return stmt;
		}

		if(knh_bytes_startsWith(op, STEXT("vacab"))) {
			tc->c += 1;
			stmt = new_StmtUVOCAB(ctx, tc);
			knh_StmtMETA_add_prestmt(ctx, stmt, &meta_tc, -1);
			return stmt;
		}

		if(ISB(op, "enum")) {
			tc->c += 1;
			stmt = new_StmtUENUM(ctx, tc);
			knh_StmtMETA_add_prestmt(ctx, stmt, &meta_tc, -1);
			return stmt;
		}

		/* using naruto.* */
		if(knh_Token_isNSCLASSN(tk)) {
			stmt = new_StmtUIMPORT(ctx, tc);
			knh_StmtMETA_add_prestmt(ctx, stmt, &meta_tc, -1);
			return stmt;
		}

		/* using Math.* */
		if(knh_Token_isCFUNCN(tk)) {
			stmt = new_StmtUFUNC(ctx, tc);
			knh_StmtMETA_add_prestmt(ctx, stmt, &meta_tc, -1);
			return stmt;
		}

		/* using Int::ns */
		if(knh_Token_isCLASSTN(tk)) {
			if(knh_bytes_startsWith(op, STEXT("Int:"))) {
				stmt = new_StmtUENUM(ctx, tc);
				knh_StmtMETA_add_prestmt(ctx, stmt, &meta_tc, -1);
				return stmt;
			}
			if(knh_bytes_startsWith(op, STEXT("Float:"))) {
				stmt = new_StmtUUNIT(ctx, tc);
				knh_StmtMETA_add_prestmt(ctx, stmt, &meta_tc, -1);
				return stmt;
			}
			if(knh_bytes_startsWith(op, STEXT("String:"))) {
				stmt = new_StmtUVOCAB(ctx, tc);
				knh_StmtMETA_add_prestmt(ctx, stmt, &meta_tc, -1);
				return stmt;
			}
		}
		if(SP(tk)->tt == TT_SEMICOLON) {
			knh_Token_perror(ctx, tk, KMSG_UUOPTION);
		}
		else {
			KNH_ASSERT(SP(tc->ts[tc->c-1])->tt == TT_USING);
			knh_Token_perror(ctx, tc->ts[tc->c-1], KMSG_UUOPTION);
			knh_tokens_nextStmt(tc);
		}
	}
	else {
		KNH_ASSERT(SP(tc->ts[tc->c-1])->tt == TT_USING);
		knh_Token_perror(ctx, tc->ts[tc->c-1], KMSG_UUOPTION);
	}

	return new_Stmt(ctx, 0, STT_DONE);
}

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

static
Stmt *new_StmtIMPLEMENTS(Ctx *ctx, knh_tokens_t *tc)
{
	Stmt *o = new_Stmt(ctx, 0, STT_IMPLEMENTS);
	if(tc->c < tc->e) {
		while(tc->c < tc->e) {
			knh_Stmt_add_CLASSN(ctx, o, tc);
			if(tc->c < tc->e && SP(tc->ts[tc->c])->tt != TT_COMMA) {
				break;
			}
			tc->c += 1;
		}
	}
	return o;
}

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

void
knh_Stmt_add_EXTENDS(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	if(SP(o)->stt == STT_ERR) return;
	if(tc->c < tc->e && SP(tc->ts[tc->c])->tt == TT_EXTENDS) {
		tc->c += 1;
		knh_Stmt_add_CLASSN(ctx, o, tc);
	}
	else {
		knh_Stmt_add_S(ctx, o, TT_TYPEN, knh_tClass[CLASS_Object].sname);
	}
	if(tc->c < tc->e && SP(tc->ts[tc->c])->tt == TT_IMPLEMENTS) {
		tc->c += 1;
		knh_Stmt_add(ctx, o, TM(new_StmtIMPLEMENTS(ctx, tc)));
	}
	else {
		knh_Stmt_add(ctx, o, TM(new_StmtDONE(ctx)));
	}
	knh_Stmt_add_STMT1(ctx, o, tc);
}

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

void
knh_Stmt_add_ANY_(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	if(SP(o)->stt == STT_ERR) return;
	if(tc->c < tc->e && knh_Token_isANY(tc->ts[tc->c])) {
		knh_Stmt_add(ctx, o, TM(tc->ts[tc->c]));
	}
	else {
		knh_Stmt_add_ASIS(ctx, o);
	}
}

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

void
knh_Stmt_add_ELSE(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	if(SP(o)->stt == STT_ERR) return;
	if(tc->c < tc->e && SP(tc->ts[tc->c])->tt == TT_ELSE) {
		tc->c += 1;
		knh_Stmt_add_STMT1(ctx, o, tc);
	}
	else {
		knh_Stmt_add(ctx, o, TM(new_StmtDONE(ctx)));
	}
}

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

void
knh_Stmt_add_CLASSTNs(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	knh_tokens_t stmt_tc, comma_tc;
	if(SP(o)->stt == STT_ERR) return;
	knh_tokens_stepFirstStmt(ctx, tc, &stmt_tc, 1);
	while(stmt_tc.c < stmt_tc.e) {
		knh_tokens_stepFirstComma(ctx, &stmt_tc, &comma_tc);
		if(comma_tc.c < comma_tc.e) {
			knh_Stmt_add_CLASSTN(ctx, o, &comma_tc);
			knh_tokens_ignore(ctx, &comma_tc);
		}
	}
}


/* ======================================================================== */
/* [stmt3] */

void
knh_Stmt_add_PSTMT3(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	Token *tk = tc->ts[tc->c];
	if(SP(o)->stt == STT_ERR) return;
	if(tc->c < tc->e && SP(tk)->tt == TT_PARENTHESIS) {
		knh_tokens_t ptc, stmt_tc;
		knh_Token_tokens_add(ctx, tk, new_Token__text(ctx, tk, STEXT(";")));
		knh_Token_tc(tc->ts[tc->c], &ptc);
		if(knh_tokens_count(&ptc, TT_COLON) > 0) {
			SP(o)->stt = STT_FOREACH;
			knh_Stmt_add_PEACH(ctx, o, tc);
			return;
		}
		else {
			int c = knh_tokens_count(&ptc, TT_SEMICOLON);
			if(c != 3) {
				knh_Stmt_perror(ctx, o, tc, KMSG_EPSTMT3);
				return ;
			}
			else {
				/* for(FIRST;second;third) */
				knh_tokens_stepFirstStmt(ctx, &ptc, &stmt_tc, 1);
				//DEBUG3_STC("ptc", ptc);
				//DEBUG3_STC("stmt_tc", stmt_tc);
				if(stmt_tc.c == stmt_tc.e) {
					knh_Stmt_add(ctx, o, TM(new_StmtDONE(ctx)));
				}
				else {
					KNH_ASSERT(SP(stmt_tc.ts[stmt_tc.e])->tt == TT_SEMICOLON);
					stmt_tc.e++;
					knh_Stmt_add(ctx, o, TM(new_StmtFUNCEXPR(ctx, &stmt_tc)));
				}
				knh_tokens_stepFirstStmt(ctx, &ptc, &stmt_tc, 1);
				//DEBUG3_STC("ptc2", ptc);
				//DEBUG3_STC("stmt_tc2", stmt_tc);
				/* for(first;SECODN;third) */
				if(stmt_tc.c == stmt_tc.e) {
					knh_Stmt_add(ctx, o, TM(new_TokenCONST(ctx, FL(tc->ts[tc->c]), KNH_TRUE)));
				}
				else {
					knh_Stmt_add(ctx, o, new_TermEXPR(ctx, &stmt_tc, KNH_RVALUE));
				}
				/* for(first;second;THIRD) */
				if(ptc.c == ptc.e-1) {
					knh_Stmt_add(ctx, o, TM(new_StmtDONE(ctx)));
				}
				else {
					KNH_ASSERT(SP(ptc.ts[ptc.e-1])->tt == TT_SEMICOLON);
					ptc.e++;
					knh_Stmt_add(ctx, o, TM(new_StmtLETEXPR(ctx, &ptc)));
				}
			}
		}
		tc->c += 1;
		knh_Stmt_add_STMT1(ctx, o, tc);
	}
	else {
		knh_Stmt_perror(ctx, o, tc, KMSG_EPSTMT3);
	}
}

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

void knh_Stmt_add_STMT1(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	if(SP(o)->stt == STT_ERR) return;
	if(tc->c < tc->e && SP(tc->ts[tc->c])->tt == TT_BRACE) {
		knh_Stmt_add(ctx, o, TM(new_StmtINSTMT(ctx, tc->ts[tc->c])));
		tc->c += 1;
	}
	else {
		knh_Stmt_add(ctx, o, TM(new_StmtSTMT1(ctx, tc)));
	}
}

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

static
Stmt *new_StmtCATCH(Ctx *ctx, knh_tokens_t *tc)
{
	Stmt *o = new_Stmt(ctx, 0, STT_CATCH);

	if(tc->c < tc->e && SP(tc->ts[tc->c])->tt == TT_PARENTHESIS) {
		knh_tokens_t p_tc;
		knh_Token_tc(tc->ts[tc->c], &p_tc);
		knh_Stmt_add_TYPEN(ctx, o, &p_tc);
		knh_Stmt_add_VARN(ctx, o, &p_tc);
		knh_tokens_ignore(ctx, &p_tc);
		tc->c += 1;
	}
	else {
		knh_Stmt_perror(ctx, o, tc, KMSG_TCATCHPARAM);
		return o;
	}
	knh_Stmt_add_STMT1(ctx, o, tc);

	if(tc->c < tc->e && SP(tc->ts[tc->c])->tt == TT_CATCH) {
		tc->c += 1;
		knh_Stmt_tail_append(ctx, o, new_StmtCATCH(ctx, tc));
	}
	return o;
}

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

void
knh_Stmt_add_CATCH(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	if(SP(o)->stt == STT_ERR) return;
	if(tc->c < tc->e && SP(tc->ts[tc->c])->tt == TT_CATCH) {
		tc->c += 1;
		knh_Stmt_add(ctx, o, TM(new_StmtCATCH(ctx, tc)));
	}
	else {
		knh_Stmt_add(ctx, o, TM(new_StmtDONE(ctx)));
	}

	if(tc->c < tc->e && SP(tc->ts[tc->c])->tt == TT_FINALLY) {
		tc->c += 1;
		knh_Stmt_add_STMT1(ctx, o, tc);
	}
	else {
		knh_Stmt_add(ctx, o, TM(new_StmtDONE(ctx)));
	}
}

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

void
knh_Stmt_add_CMETHOD(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	if(SP(o)->stt == STT_ERR) return;
	if(tc->c < tc->e) {
		Token *tkc = tc->ts[tc->c];
		if(SP(tkc)->tt == TT_CMETHODN) {
			knh_Stmt_add(ctx, o, TM(tkc));
			knh_Stmt_add(ctx, o, TM(tkc));
			tc->c += 1;
			goto L_PARAMS;
		}
		else if(SP(tkc)->tt == TT_NAME && knh_Token_isMETHODN(tkc)) {
			knh_Stmt_add_ASIS(ctx, o);
			knh_Stmt_add(ctx, o, TM(tkc));
			tc->c += 1;
			goto L_PARAMS;
		}
		else if(SP(tkc)->tt == TT_PARENTHESIS) {
			knh_Stmt_add_ASIS(ctx, o);
			knh_Stmt_add(ctx, o, TM(new_TokenMN(ctx, FL(tkc), METHODN_new)));
			//tc->c += 1;
			goto L_PARAMS;
		}
	}
	knh_Stmt_perror(ctx, o, tc, KMSG_TMETHODN);
	return;

	L_PARAMS:;
	knh_Stmt_add_PARAMs(ctx, o, tc);
	knh_Stmt_add_STMT1(ctx, o, tc);
}

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

static
void knh_Stmt_add_TYPEVARN(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	Token **ts = tc->ts;
	if(SP(o)->stt == STT_ERR) return;

	if(tc->c < tc->e) {
		if(knh_Token_isVARN(ts[tc->c])) {
			knh_Stmt_add(ctx, o, TM(new_TokenASIS(ctx, FL(ts[tc->c]))));
			knh_Stmt_add(ctx, o, TM(ts[tc->c]));
			tc->c += 1;
			return;
		}
		if(knh_Token_isTYPEN(tc->ts[tc->c])) {
			knh_Stmt_add(ctx, o, TM(tc->ts[tc->c]));
			tc->c += 1;
			knh_Stmt_add_VARN(ctx, o, tc);
			return;
		}
	}
	knh_Stmt_perror(ctx, o, tc, KMSG_TTYPEN);
}

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

void
knh_Stmt_add_PARAMs(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	if(SP(o)->stt == STT_ERR) return;
	if(tc->c < tc->e && SP(tc->ts[tc->c])->tt == TT_PARENTHESIS) {
		knh_tokens_t param_tc;
		Stmt *stmt = NULL;
		knh_Token_tc(tc->ts[tc->c], &param_tc); tc->c += 1;
		if(param_tc.c == param_tc.e) {
			knh_Stmt_add(ctx, o, TM(new_StmtDONE(ctx)));
			return;
		}
		while(param_tc.c <param_tc.e) {
			Stmt *o2 = new_Stmt(ctx, 0, STT_DECL);
			knh_tokens_t ptc;
			knh_tokens_stepFirstComma(ctx, &param_tc, &ptc);
			knh_StmtMETA_add(ctx, o2, &ptc);
			knh_Stmt_add_TYPEVARN(ctx, o2, &ptc);
			if(ptc.c + 1 < ptc.e && SP(ptc.ts[ptc.c])->tt == TT_LET) {
				ptc.c += 1;
				knh_Stmt_add(ctx, o2, new_TermEXPR(ctx, &ptc, KNH_RVALUE));
			}
			else {
				knh_tokens_ignore(ctx, &ptc);
				knh_Stmt_add_ASIS(ctx, o2);
			}
			if(stmt != NULL) {
				knh_Stmt_tail_append(ctx, stmt, o2);
			}
			else {
				stmt = o2;
			}
		}
		knh_Stmt_add(ctx, o, TM(stmt));
		return ;
	}
	else {
		knh_Stmt_perror(ctx, o, tc, KMSG_TARGS);
	}
}


/* ------------------------------------------------------------------------ */
/* [method] */

Stmt *new_StmtMETHOD(Ctx *ctx, knh_tokens_t *tc)
{
	Stmt *o = new_Stmt(ctx, 0, STT_METHOD);
	knh_StmtMETA_add_prestmt(ctx, o, tc, -1);
	knh_Stmt_add_TYPEN(ctx, o, tc); /* TYPEN */
	knh_Stmt_add_CMETHOD(ctx, o, tc); /* cmethod */
	return o;
}

/* ======================================================================== */
/* [foreach] */

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

//static
//void knh_Stmt_add_SELECT(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
//{
//
//}

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

static
void knh_Stmt_add_FROM(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	if(SP(o)->stt == STT_ERR) return;
	knh_Stmt_add(ctx, o, new_TermEXPR(ctx, tc, KNH_RVALUE));
}

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

static
void knh_Stmt_add_WHERE(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	if(SP(o)->stt == STT_ERR) return;
	if(tc->c < tc->e) {
		knh_Stmt_add(ctx, o, new_TermEXPR(ctx, tc, KNH_RVALUE));
	}
	else {
		knh_Stmt_add(ctx, o, TM(new_TokenCONST(ctx, FL(o), KNH_TRUE)));
	}
}

/* ------------------------------------------------------------------------ */
// foreach(String n from source[] where p) {} */
// foreach(select name, age from (HashMap).opItr2()[] where p) {} */
// foreach(n from a [] where p) {}
// foreach(n from

void
knh_Stmt_add_PEACH(Ctx *ctx, Stmt *o, knh_tokens_t *tc)
{
	Token *tk = tc->ts[tc->c];
	if(SP(o)->stt == STT_ERR) return;
	if(tc->c < tc->e && SP(tk)->tt == TT_PARENTHESIS) {
		knh_tokens_t ptc;
		knh_tokens_t val_tc, from_tc, where_tc;
		int i;
		knh_Token_tc(tc->ts[tc->c], &ptc);
		for(i = 0; i < ptc.e; i++) {
			if(SP(ptc.ts[i])->tt == TT_FROM || SP(ptc.ts[i])->tt == TT_COLON
					|| (i > 0 && SP(ptc.ts[i])->tt == TT_NAME && ISB(knh_Token_tobytes(ptc.ts[i]), "in"))) {
				val_tc.c = 0;
				val_tc.e = i;
				val_tc.ts = ptc.ts;
				i++;
				goto L_FROM;
			}
		}
		DEBUG3("NO FROM!!");
		knh_Stmt_perror(ctx, o, tc, KMSG_EPEACH);
		return;

		L_FROM:;
		from_tc.c = i;
		from_tc.e = ptc.e;
		from_tc.ts = ptc.ts;
		where_tc.c = ptc.e;
		where_tc.e = ptc.e;
		where_tc.ts = ptc.ts;
		for(i = 0; i < ptc.e; i++) {
			if(SP(ptc.ts[i])->tt == TT_WHERE || SP(ptc.ts[i])->tt == TT_SEMICOLON) {
				from_tc.e = i;
				where_tc.c = i + 1;
				break;
			}
		}
		if(!(from_tc.c < from_tc.e)) {
			DEBUG3("NO FROM!! c=%d, e=%d", from_tc.c, from_tc.e);
			knh_Stmt_perror(ctx, o, tc, KMSG_EPEACH);
			return;
		}

		if(knh_tokens_count(&val_tc, TT_COMMA) > 0) {
			TODO();
			knh_Stmt_add_TYPEVARN(ctx, o, &val_tc);
			knh_tokens_ignore(ctx, &val_tc);
		}else {
			knh_Stmt_add_TYPEVARN(ctx, o, &val_tc);
			knh_tokens_ignore(ctx, &val_tc);
		}
		knh_Stmt_add_FROM(ctx, o, &from_tc);
		knh_Stmt_add_WHERE(ctx, o, &where_tc);
		tc->c += 1;
		knh_Stmt_add_STMT1(ctx, o, tc);
	}
	else {
		knh_Stmt_perror(ctx, o, tc, KMSG_EPEACH);
	}
}
/* ------------------------------------------------------------------------ */


#ifdef __cplusplus
}
#endif
