/*---------------------------------------------------------------------------*/
/* トークンの中間コード化（中間コード作成：切り放し不可）*/

#include <stdio.h>
#include <stdlib.h>
#include "stk.h"
#include "abuff.h"

extern STKSYS	*stksys;

static unchar	*ctop;	// 中間コードエリア先頭
static unchar	*ctext;	// 中間コードストア位置
static long	csize;	// 中間コードエリアサイズ

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

static int CodeResize(int of,unchar code)
/* 中間コード領域を拡大（判定）する＋中間コード識別子＋長さをストアする */
// ofはuncharの範囲でのみ有効
{
	/* ofには中間コード+len(unchar*2)を除いたサイズを与えること */
	if (ctext+sizeof(code)+LCHAR+of>=ctop+csize) {
	    unchar *text;
		// 現在の中間コードエリアに入りきらないので拡大する
		// （エリアが移動する場合有り）
		csize+=ACSIZE;
		if ((text=realloc(ctop,csize))==NULL) {
			/* 拡大できない */
			return(CODEAREA);
		}
		// ストア位置と先頭の変更
		ctext=text+(ctext-ctop);
		ctop=text;
	}
	// 中間コード識別子+lenをストアする
	// （これで識別子部分が偶数バイト化される）
	*ctext++=code;
	*ctext++=(unchar)of;
	return(OK);
}

static int StoreCode1(unchar code,long num)
/* 中間コードストア:type1 */
// 数値・コマンド（通常）・ジャンプアドレス・コメント・変数など
// □□ □□□□:tbl,num,adrs
{
int ret;
	ret=CodeResize(sizeof(num),code);
	if (!ret) {
	    long *ltext;
		ltext=(long *)ctext;
		*ltext++=num;
		ctext=(unchar *)ltext;
		// *(long *)ctext=num;
		// ctext+=LLONG;
	}
	return(ret);
}

static int StoreCode12(unchar code,long num)
/* 中間コードストア:type1-2 */
/* コマンド（ジャンプアドレス付き）*/
// □□ □□□□:tbl □□□□:adrs
{
int ret;
	ret=CodeResize(sizeof(num)+LLONG,code);
	if (!ret) {
	    long *ltext;
		ltext=(long *)ctext;
		//num NULLの順
		*ltext++=num;
		*ltext++=(long)NULL;
		ctext=(unchar *)ltext;
	}
	return(ret);
}

static int StoreCode2(unchar code,unchar len,unchar *name)
/* 中間コードストア:type2 */
// 固定文字列
// □ □:len □〜:内容
{
int ret,f;
	f=len&1;
	ret=CodeResize(len+f,code);
	if (!ret) {
		// memcpy(ctext,name,(int)len);
		// ctext+=len;
		// 上の書き方より下のwhileの方が小さいのでそうする
		while (len-->0) *ctext++=*name++;
		if (f) *ctext++=0x00; // 偶数バイト化処理
	}
	return(ret);
}

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

static int STKcode1(unchar *token,VARSYS2 *label,VARSYS2 *Gvar,unchar *btext)
/* tokenを中間コードに変換する */
/* token -> ctext:中間コードエリア */
// btext : このトークンのテキスト中の開始アドレス
// token長はTKLENにより、STKTokenGet内でチェックされている
{
int ret,len;
long num;
short p;
unsint type;
unchar ce,cs;
unchar *vnadrs;
	len=strlen(token); /* token長 */
	ce=token[len-1]; /* 末尾文字 */
	
	if (ce==SYMLB) { /* label: ＝ ラベル:登録と同時に確定される */
		if (len==1) return(LBLNMERR); /* ':'しかなかった */
		token[len-1]=0; /* ':'を消す(EOS) */
		if (STKCheckName(token)) return(LBLNMERR); // ﾗﾍﾞﾙ名がおかしい
		/* ラベルを登録する：ラベルは再定義不可 */
		if ((ret=VarNameBuff(token,&vnadrs,label))) return(ret);
		p=VarDefine(vnadrs,VDAI,label);
		if (p<0) return((int)-p); /* error */
		/* 確定（現在の中間コードテキストオフセットアドレス）*/
		VarNumWrite(p,(long)(ctext-ctop),BIT15,label);
		// ラベルはテキスト参照コメントとする（デバッグ用）
		return(StoreCode1(SCCOMT,(long)btext));
	}
	
	/* 固定文字列処理
	   V2以降では、文字列は中間コードエリア内にそのまま置かれ、
	   V1で言うところの一時文字列エリアには記録されない。
	   これに伴い、V2では一時文字列領域はなくなった。
	   また、V2.3からは長さがuncharサイズ(TKLEN)になった。
	*/
	cs=0;
	if (token[1]==SYMSTR) { // モード付き固定文字列の可能性有り
		// 特殊文字列のモードチェック
		// ?"〜"は、?がモードとなる
		cs=token[0];
		if (cs!=SYMSTR) {
			if (cs!=SYMCSTR) return(STR1ERR);
			// ."〜" = コード変換無し
			token++; // .を飛ばす -> "を先頭にする
			len--; // .の分を引く
		} // ""はここでは処理させない
	}
	if (token[0]==SYMSTR) { // 固定文字列
		token++; // 前の"を飛ばす
		// 後ろの"を消す（後ろからサーチ）
		// 固定文字列が閉じていない場合はSTKTokenGet()でエラーになっているので
		// ここでは必ず閉じている
		while (token[--len]!=SYMSTR);
		token[len]=0;
		/* ""は空文字列になる */
		// コメントでテキスト参照を書き込む（デバッグ用）
		if ((ret=StoreCode1(SCCOMT,(long)btext))) return(ret);
		/* ここでは一旦tokenの中に変換後文字列を入れる */
		/* サイズ判定のため、直接中間コードエリアに書き込めない */
		/* ２重転送だがしかたない */
		len=jcstring(token,token,(cs==0? SYMEXT:0))+1; /* +EOS */
		return(StoreCode2(SCSTR1,(unchar)len,token));
	}
	
	/* 数値判別 */
	switch (atoi3(token,&num,BIT15)) { /* １文字は'〜'で取り込む */
	    case -3: return(VSCHERR); /* '〜'が閉じていない */
	    case  0: /* 数字であった */
		// コメントでテキスト参照を書き込む（デバッグ用）
		if ((ret=StoreCode1(SCCOMT,(long)btext))) return(ret);
		return(StoreCode1(SCNUM,num));
	}
	/* 数字ではなかった */
	/* 数字として不十分な場合もコマンドなどの場合があるので以下へ続く */
	
	/* コマンドチェック */
	{ STKCOM *tbl;
	ret=CheckCommand(token,&tbl);
	if (ret) { /* ｺﾏﾝﾄﾞであった */
		if (tbl[ret].ret&CFJUMP) { /* このコマンドはジャンプアドレス付きである */
			return(StoreCode12((unchar)(FJUMP|ret),(long)tbl));
		}
		return(StoreCode1((unchar)ret,(long)tbl));
	}
	}
	/* コマンドでない → ラベルか変数 */
	
	if (ce==SYMCL) { /* 末尾に'.'が有るものは、ラベルcallである */
		/* ﾗﾍﾞﾙ名サーチのために印('.')を消す */
		token[--len]=0;
	} else ce=0; // ラベルcallでない
	
	// 確定済みラベルかどうかを調べる
	p=VarSearch(token,label,&type);
	if (p>0) { /* 確定済みラベルである */
		VarNumRead(p,&num,&type,label);
		return(StoreCode1((ce? SCCALL:SCLBL),num));
	}
	// 変数名か未定義ラベルである
	// とりあえず変数名/未定義callﾗﾍﾞﾙとして登録する
	// 名前チェック
	if (STKCheckName(token)) return(ce? LBLNMERR:VARILL);
	// 定義済み変数かどうかを調べる
	p=VarSearch(token,Gvar,&type);
	if (p<0) p=-p; // 定義済み
	else { // p>0は有り得ない（中間コード時には確定しないから）
		// 名前記録
		if ((ret=VarNameBuff(token,&vnadrs,Gvar))) return(ret);
		// 定義（確定は不要というか実行時まで確定しない）
		p=VarDefine(vnadrs,(ce? VLABEL:0),Gvar);
		if (p<0) return((int)-p); /* error */
	}
	// 中間コードストア（その変数の格納されたポイント）
	return(StoreCode1((ce? SCCAL0:SCVAR),(long)p));
}

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

static int STKcode(unchar *text,VARSYS2 *label,VARSYS2 *Gvar)
/* テキストの中間コード化（全体）
	text:テキストエリア -> ctop:中間コードエリア */
{
unchar token[TKLEN+1],*btext,c0,c1;
int ret;
short cmt=0;
	/* 中間コードエリア確保（初期）*/
	csize=CSIZE; /* csize:総サイズ */
	if ((ctop=newmalloc(csize))==NULL) return(CODEAREA);
	ctext=ctop;
	
	/* トークン解析 */
	while (!(ret=STKTokenGet(token,&text,&btext))) {
	    #if DEBUG == 2
	    printf2("[%s]",token);
	    #endif
	    // 行内コメントチェック（ネスティング可）
	    c0=token[0];
	    c1=token[1];
	    if (c0==SYMCMTS && c1==SYMCMTE) {
		if (cmt>=CMTMAX) { /* コメントが深すぎる */
			ret=CMTERR2;
			break;
		}
		cmt++;
		continue;
	    }
	    if (c0==SYMCMTE && c1==SYMCMTS) {
		if (cmt==0) { /* コメント最初がないのに閉じがある */
			ret=CMTERR1;
			break;
		}
		cmt--;
		continue;
	    }
	    if (cmt==0) { // コメント中ではない
	    	// １トークン中間コード化
	    	SetErrToken(token);
	    	if ((ret=STKcode1(token,label,Gvar,btext))) break;
	    }
	}
	/* 中間コード終わり */
	#if DEBUG
	printf2("\n");
	#endif
	if (ret==ENDOK) ret=OK; /* 正常終了 */
	if (ret) free(ctop); /* エラー発生時には中間コードエリアも解放する */
	else ret=CodeResize(0,SCEND); // End of TEXT
	return(ret);
}

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

#if DEBUG2 >= 2
static void STKrcode(unchar *ctext)
/* デバッグモジュール：中間コードからテキストを作る */
{
int c,i=0;
unchar *txt;
FILE *fp;
	fp=fopen("_STEXT.stk","wt");
	if (fp!=NULL) {
		while (1) {
			txt=RemakeToken(ctext);
			if (*txt!=0) { /* 空文字列は出力しない */
				fputs(txt,fp);
				fputc(' ',fp);
				if ((++i)==5) {i=0; fputc('\n',fp);}
			}
			c=*ctext++;
			if (c==SCEND) break;
			ctext+=EvenLen(*ctext++); // 偶数バイト化付き
		}
		fputc('\n',fp);
		fclose(fp);
	}
}
#endif

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

int STKSemiCompile(unchar *text,unchar *(*ctext0))
/* テキストの中間コード化（前後処理も含む）
   stkmain()実行前にこれでテキストを中間コード化しておく必要がある

-> text  :テキストアドレス
<- ctext0:中間コード領域アドレス

  処理順
	コマンドハッシュの確保＆確定:stkcom
	  ラベル記録エリアの確保＆初期化:STKSemiCompile
	    中間コードエリアの確保&ラベルの定義&中間コード化:stkcode
            未確定ラベルの確定と絶対アドレス化:stklabel
	  ラベル記録エリアの解放:stkcode
	コマンドハッシュの解放:stkcom
	-> 実行時にはトークンハッシュ／ラベル領域は不要。
*/
{
VARSYS2 label;
int ret;
STKSYS *sys;
	sys=stksys; // 省メモリーのためローカル変数に置き換える
	/* ラベル領域確保 */
	/* ラベルは本来unchar *であるが、処理上は32bit-intとして処理する */
	if (sys->llmax>SCLMAX ||
	    VarOpen(sys->lsize,sys->llmax,0,&label)) return(LBLOPEN); // BIT0=0で処理
	
	/* コマンド高速検索テーブル作成 */
	ret=OpenCommand();
	if (!ret) {
	    /* 中間コード化 */
	    ret=STKcode(text,&label,&sys->Gvar); // エラー発生時には中間コードは出来ていない
	    #if DEBUG
	    VarDump(16,&label);
	    #endif
	    if (!ret) {
		/* ラベルの確定 */
		ret=STKSetLabelAdrs(ctop,&label);
		#if DEBUG2 >= 2
		/* 中間コードからテキストを逆生成する */
		STKrcode(ctop);
		#endif
	    }
	    *ctext0=ctop;
	    /* コマンドテーブル解放 */
	    CloseCommand();
	}
	/* ラベル領域解放（実行時には不要だから）*/
	VarClose(&label);
	
	#if DEBUG2 >= 1
	/* 中間コードをファイルに出力する */
	{ FILE *fp;
	fp=fopen("STKcode.DAT","wb");
	if (fp!=NULL) {
		fwrite(ctop,(int)(ctext-ctop),1,fp);
		fclose(fp);
	}
	}
	#endif
	return(ret);
}

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