/*****************************************************************************/
/* ＳＴＫコマンド：変数処理（切り離せるがしない方がいい）*/
/*****************************************************************************/

#include "stk.h"
#include "abuff.h"

extern STKSYS *stksys;

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

static void STKVarDef1(NSTACK *nstk,int mode,long num)
// １変数定義 : mode : 0=数値 , 1=固定文字列
{
STKSYS *sys;
VAR0 *lv;
	// 確定
	// STKChgVarNum()からここに来るNVADRはすべてﾛｰｶﾙ定義に変更する
	sys=stksys; // 省メモリーのためローカル変数に置き換える
	lv=&sys->Lvar[(nstk->loc=sys->loc)][nstk->point];
	lv->type=(mode? VSTR1:VNUM);
	lv->num=num;
}

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

static int Svar(NSTACK nstk[],int mode)
/* １変数の定義 */
/* 実際の変数登録はトークン解析内で行われるので、ここではその確定を行う */
/* 確定をされていない変数は、アクセスできない */
/* var(NVADRN:0) VAR/SVAR → var(0:NVADR) */
/* mode : 0=数値 , 1=固定文字列 */
{
	STKVarDef1(&NS0,mode,0L);
	// スタック上には確定された変数そのものが残る
	// name VAR 数値 = / name SVAR "固定文字列" = で定義と代入ができる
	NN0=0; // 初期値を0/空文字列とする
	NA0=tolower(NA0)|(mode? (VSTR1<<8):(VNUM<<8)); // 確定
	return(OK);
}

static int Svars(NSTACK nstk[],int mode)
/* 変数の連続定義 */
/* ( var1 var2 ... ).(0) VARS/SVARS → なし */
/*			 VARS=/SVARS= : スタックから自動読みだし代入付き */
/* mode : 0=VARS:数値 , 1=SVARS:固定文字列 , BIT15=代入付き */
{
NSTACK ns;
int ret;
long num=0L; // 0L=仮代入値(スタックからの代入がないときのため）
short i,cn;
unsint fdai;
STKSYS *sys;
	cn=(short)NN0; // 代入は最大でもnstackの段数までだから
	if (cn<=0L) return(VARNOILL);
	sys=stksys; // 省メモリーのためローカル変数に置き換える
	fdai=(mode&BIT15);
	if (fdai) mode^=BIT15;
	for (i=0;i<cn;i++) {
		if ((ret=PopNStack(&ns))) return(ret);
		switch ((unchar)ns.atrb) {
		    case NVADR:// ローカル
			if (ns.loc==stksys->loc) return(VARDBL);
			// その変数が現在と違う段の時は現在の段に定義する
		    case NVADRN: // 未定義変数ならＯＫ
			break;
		    default: // 変数以外はエラー
			return(ARGILL);
		}
		if (fdai) { // スタックから代入モード
		    NSTACK *n;
			// 本当はスタックを直接操作するのはよろしくないが、
			// こうしないと複雑になりすぎるのでしょうがない
			if (sys->ntop<cn) return(NSTKEMP);
			n=&sys->nstack[sys->ntop-cn];
			if ((ret=STKVarChange(n,0))) return(ret);
			if (n->atrb!=(mode? NSTR1:NNUM)) {
				// 変数は数値と固定文字列以外は不可
				#if DEBUG
				printf2("mode=%d,atrb=%c\n",mode,n->atrb);
				#endif
				return(ARGILL);
			}
			num=n->num;
		}
		STKVarDef1(&ns,mode,num);
	}
	// 代入した分のスタックも削除する
	// 代入中にエラーが出なかったということは、大丈夫ということ
	if (fdai) sys->ntop-=cn;
	return(OK);
}

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

static int Sstore(NSTACK nstk[],int mode)
/* 変数への代入（変数は確定済みのこと）*/
	/* var(NVADR:1) n(0) =  → なし */
	/* var(NVADR:1) n(0) =. → n(0) */
	/* nはNNUM/NSTR1が可能(varの定義時の型による) */
/* 変数ロック／アンロック */
	/* var(NVADR:0) VLOCK/VUNLOCK → なし */
	/*（配列変数は強制的にLOCKされる）*/
// グローバル参照
	// var(NVADR:0) %% → var(NVADR)
{
char m;
NSTACK *ns;
STKSYS *sys;
unsint type;
VAR0 *lv;
	sys=stksys; // 省メモリーのためローカル変数に置き換える
	ns=&nstk[mode<2];
	if (mode==4) { // ﾛｰｶﾙ %%はグローバル変数となる
		// pointは変わらない（ＳＴＫの変数記録法がそうなっているから）
		lv=&sys->Lvar[0][ns->point]; // [0]=ｸﾞﾛｰﾊﾞﾙ
		if (lv->type&VKAKU) return(VARNDEF); // グローバル変数は未定義
		NA0=((lv->type&VTYPE)<<8)|NVADR;
		NN0=lv->num;
		nstk[0].loc=0; // グローバル
		return(OK);
	}
	lv=&sys->Lvar[ns->loc][ns->point];
	type=lv->type;
	m=ns->atrb>>8; // 変数タイプ読み取り
	if (mode>=2) { // ロック／アンロック
		// 数値／固定文字列以外は設定出来ない（配列は常にロック状態）
		if (m!=VNUM && m!=VSTR1) return(VTYPEERR);
		lv->type=(mode==2? (type|VDAI):(type&(~VDAI)));
	} else { // 代入：ここで代入出来るのは数値/固定文字列変数のみ
		if (!(	(m==VNUM  && NA0==NNUM)
		    ||	(m==VSTR1 && NA0==NSTR1) )) return(VTYPEERR);
		// '=.'ではスタック上には代入した数値そのものが残る
		if (type&VDAI) return(VARCSTA);
		lv->num=NN0;
	}
	return(OK);
}

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

static int Slocal(NSTACK nstk[],int mode)
// ローカル変数エリア宣言:{ 〜 }
// ローカルエリアの外にgotoで飛び出さないこと:終了は}.を使う
{
short loc;
VAR0 *lv;
STKSYS *sys;
#ifndef NOTRACE
unchar mes[80];
#endif
	sys=stksys; // 省メモリーのためローカル変数に置き換える
	loc=sys->loc;
	if (!mode) { // ローカルエリア開始
	    int i;
		if (++loc>=sys->locmax) return(LOCOVER); /* 深すぎる */
		lv=sys->Lvar[loc];
		if (lv==NULL) { // まだ一度もこの段は使っていない
			// 確保
			lv=sys->Lvar[loc]=(VAR0 *)newmalloc(sizeof(VAR0)*sys->Gvar.vpoint);
			if (lv==NULL) return(LOCOPEN);
		} // 一度でも使ったことのある段はtypeを全てVKAKU(未確定)にするのみ
		for (i=0;i<sys->Gvar.vpoint;i++) lv[i].type=VKAKU;
		#ifndef NOTRACE
		if (sys->mode&MDftrace0) {
			sprintf2(mes,"{%d段",loc);
			TracePrint(mes);
		}
		#endif
	} else { // ローカルエリア終了
		// freeはSTK終了時にstkmainで行うので、ここではこれといった処理はしない
		if (loc==0) return(LOCNONE); /* ローカルはない */
		#ifndef NOTRACE
		if (sys->mode&MDftrace0) {
			sprintf2(mes,"}%d段",loc);
			TracePrint(mes);
		}
		#endif
		loc--;
	}
	sys->loc=loc;
	return(OK);
}

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

static int Slocal2(NSTACK nstk[],int mode,unchar *text,unchar *(*jump),unchar *(*go))
// ローカル変数エリア終了へ飛ぶ:}.
{
extern STKCOM vartbl[]; // 中間コードを参照するため
	if (*jump==NULL) { /* ジャンプ先がわかっていないとき */
		if (STKSearchPair(vartbl,1,0,2,0,&text,jump)) return(LOCNONE);
		// ローカルエリア終了処理が必要なので、}そのものへ飛ぶ。
	}
	*go=*jump;
	return(LOK);
}

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

// ここの中だけで特有なエラー
static ERRMES ERRv[]={
	 {VTYPEERR	,"変数の型が違う"}
	,{VARNOILL	,"変数指定数異常"}
	,{LOCOVER	,"ﾛｰｶﾙｴﾘｱが深すぎる"}
	,{LOCNONE	,"ﾛｰｶﾙｴﾘｱはない"}
	,{LOCERR	,"ﾛｰｶﾙｴﾘｱが閉じてない"}
	,{0,NULL}
};
static ERLINK erlist={ERRv,NULL};

static int VarInit()
/* 変数処理：初期化 */
{
	return(STKErrLink(&erlist));
}

static int VarEnd()
/* 変数処理：終了 */
{
	// 現在の変数システムでは未確定チェックが出来ない
	// ローカル用の名前、ラベルは未確定のまま残るから
	/* {〜}が閉じているかチェック */
	return(stksys->loc>0? LOCERR:OK);
}

/*****************************************************************************/
/* コマンドテーブル */

STKCOM vartbl[]={
	 {NULL		,VarInit,TNONE	,0,0}
	/* ローカル変数 */
	// { }の位置を参照するため、この位置を変更しないこと
	,{"{"		,Slocal	,TNONE	,0,0}		// ﾛｰｶﾙｴﾘｱ開始
	,{"}"		,Slocal	,TNONE	,0,1}		// ﾛｰｶﾙｴﾘｱ終了
	,{"}."		,Slocal2,TNONE	,CFJUMP,0}	// ﾛｰｶﾙｴﾘｱ終了へ飛ぶ
	/* 数値変数定義(未確定→確定) */
	,{"VAR"		,Svar	,TVADRN	,1,0}		// 単一
	,{"VARS"	,Svars	,TNUM	,0,0}		// 連続
	,{"VARS="	,Svars	,TNUM	,0,BIT15}	// 連続＆代入
	/* 固定文字列変数定義(未確定→確定) */
	,{"SVAR"	,Svar	,TVADRN	,1,1}		// 単一
	,{"SVARS"	,Svars	,TNUM	,0,1}		// 連続
	,{"SVARS="	,Svars	,TNUM	,0,BIT15|1}	// 連続＆代入
	/* 変数代入 */
	,{"="		,Sstore	,TANO2 TVADR,0,0}	//（スタック消し）
	,{"=."		,Sstore	,TANO2 TVADR,1,1}	//（スタック残し）
	/* 変数代入制御 */
	,{"VLOCK"	,Sstore	,TVADR	,0,2}		// ロック
	,{"VUNLOCK"	,Sstore	,TVADR	,0,3}		// アンロック
	// スコープ解決
	,{"%%"		,Sstore	,TVADR	,1,4}		// グローバル変数参照
	,{NULL		,VarEnd	,TNONE	,0,0}
};

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