/*
 * スタックマシン？になっていればいいのだけれど・・・
 *
 * 1行目:
 *		使う変数名列挙
 * 		変数名はアルファベット(A-Z)１文字で定義される
 *		(大文字小文字は区別されない、重複定義は許されない)
 *		アルファベット以外の文字は無視される
 * 2行目以降:
 *		変数への代入式 or 変数出力コマンド を 1行単位で書く
 *		変数代入式は 変数名= で開始される
 *      	代入式右辺は 整数0-9 変数名A-Z 演算子*+-/ で構成され
 *      	上記以外の文字は許されない
 *      編集出力コマンドは @変数名 で記述される
 */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

/*
 * 命令コード
 *
 * POP1 ... 1回目のpopで得られる値
 * POP2 ... 2回目のpopで得られる値
 */
#define CMD_END		(0)	/* プログラムの終わり */
#define CMD_PUSH  	(1) /* 次のコードを値としてpushする */
#define CMD_LOAD  	(2) /* POP1のアドレスの変数の値をpushする */
#define CMD_PRINT 	(3) /* POP1の値を出力する */
#define CMD_ADD		(4) /* POP2+POP1の結果をpushする */
#define CMD_SUB		(5) /* POP2-POP1の結果をpushする */
#define CMD_MUL		(6) /* POP2*POP1の結果をpushする */
#define CMD_DIV		(7) /* POP2/POP1の結果をpushする */
#define CMD_STORE	(8) /* POP1の値をPOP2のアドレスの変数へ格納する */

char cmd[][9] = {"END", "PUSH", "LOAD", "PRINT", "ADD",
	"SUB", "MUL", "DIV", "STORE"};

/*
 * 構文解析遷移状態
 */
#define TERM_DEF  	(0) /* 変数定義 */
#define TERM_TOP  	(1) /* 行頭 */
#define TERM_PRT  	(2) /* 出力変数名の読み込み */
#define TERM_SET  	(3) /* 代入文字=の読み込み */
#define TERM_NUM	(4) /* 数字の読み込み */
#define TERM_SYB	(5) /* 演算子の読み込み */
#define TERM_EXP	(6) /* 式の開始の読み込み */
#define TERM_LF   	(99) /* 行末文字の読み込み*/

/*
 * プログラム終了コード
 */
#define EXST_SUCCESS	(0) /* 成功 */
#define EXST_RTERR		(1) /* 実行時エラー */
#define EXST_NFEND		(2) /* 終了命令なくプログラムの終了 */

#define SYNTAX_ERR (0)

int func1(FILE*,int[]);	/* ソースコードをコンパイルする */
void func2(int[], int);	/* コンパイルされたコードを出力する */
int func3(int[], int);	/* コンパイルされたコードを実行する */ 

int main(void)
{
	int p[1000] = {0};
	int c, r;
	
	c = func1(stdin, p);

	if (c == SYNTAX_ERR)
	{
		printf("Syntax Error\n");
		return 0;
	}
	printf("--Result--\n");
	r = func3(p, c);
	printf("--Exit Status--\n");
	switch (r)
	{
		case EXST_SUCCESS:
		{
			printf("Success\n");
			break;
		}
		case EXST_RTERR:
		{
			printf("Runtime Error\n");
			break;
		}
		case EXST_NFEND:
		{
			printf("Not found END command\n");
			break;
		}
	}
	printf("--Program Code--\n");
	func2(p, c);
	
	return 0;
}

int func1(FILE *file, int p[])
{
	int w[100];
	int v[30] = {0};
	int c, i = 0, bi = 0, j, wi = 0;
	int t = TERM_DEF;
	int vr = 0, nm;
	while ((c = getc(file)) != EOF)
	{
		switch (t)
		{
			case TERM_DEF:
			{
				if (c == '\n')
				{
					if (i == 0)
					{
						printf("term: %d\n", t);
						return SYNTAX_ERR;
					}
					p[bi++] = i;
					for (j = 0; j < i; j++)
					{
						p[bi++] = 0;
					}
					t = TERM_TOP;
				}
				else if (isalpha(c))
				{
					vr = toupper(c) - 'A';
					if (v[vr] != 0)
					{
						printf("term: %d, %c\n", t, c);
						return SYNTAX_ERR;
					}
					v[vr] = ++i;
				}
				break;
			}
			case TERM_TOP:	
			{
				if (c == '@')
				{
					t = TERM_PRT;
				}
				else if (isalpha(c))
				{
					vr = toupper(c) - 'A';
					if (v[vr] == 0)
					{
						printf("term: %d, char '%c'\n", t, c);
						return SYNTAX_ERR;
					}
					p[bi++] = CMD_PUSH;
					p[bi++] = v[vr];
					t = TERM_SET;
				}
				else
				{
					printf("term: %d, char '%c'\n", t, c);
					return SYNTAX_ERR;
				}
				break;
			}
			case TERM_PRT:
			{
				if (isalpha(c))
				{
					vr = toupper(c) - 'A';
					if (v[vr] == 0)
					{
						printf("term: %d, char '%c'\n", t, c);
						return SYNTAX_ERR;
					}
					p[bi++] = CMD_PUSH;
					p[bi++] = v[vr];
					p[bi++] = CMD_LOAD;
					p[bi++] = CMD_PRINT;
					t = TERM_LF;
				}
				else
				{
					printf("term: %d, char '%c'\n", t, c);
					return SYNTAX_ERR;
				}
				break;
			}
			case TERM_SET:
			{
				if (c == '=')
				{
					t = TERM_EXP;
				}
				else
				{
					printf("term: %d, char '%c'\n", t, c);
					return SYNTAX_ERR;
				}
				break;
			}
			case TERM_EXP:
			{
				if (isalpha(c))
				{
					vr = toupper(c) - 'A';
					if (v[vr] == 0)
					{
						printf("term: %d, char '%c'\n", t, c);
						return SYNTAX_ERR;
					}
					p[bi++] = CMD_PUSH;
					p[bi++] = v[vr];
					p[bi++] = CMD_LOAD;
					t = TERM_SYB;
				}
				else if (isdigit(c))
				{
					nm = c - '0';
					t = TERM_NUM;
				}
				else
				{
					printf("term: %d, char '%c'\n", t, c);
					return SYNTAX_ERR;
				}
				break;
			}
			case TERM_SYB:
			{
				switch (c)
				{
					case '+':
					case '-':
					{
						while (wi > 0)
						{
							p[bi++] = w[wi--];
						}
						w[++wi] = c == '+' ? CMD_ADD : CMD_SUB;
						t = TERM_EXP;
						break;
					}
					case '*':
					case '/':
					{
						while (w[wi] == '*' || w[wi] == '/')
						{
							p[bi++] = w[wi--];
						}
						w[++wi] = c == '*' ? CMD_MUL : CMD_DIV;
						t = TERM_EXP;
						break;
					}
					case '\n':
					{
						while (wi > 0)
						{
							p[bi++] = w[wi--];
						}
						p[bi++] = CMD_STORE;
						t = TERM_TOP;
						break;
					}
					default:
					{
						printf("term: %d, char '%c'\n", t, c);
						return SYNTAX_ERR;
					}
				}
				break;
			}
			case TERM_NUM:
			{
				if (isdigit(c))
				{
					nm = nm * 10 + (c - '0');
				}
				else
				{
					p[bi++] = CMD_PUSH;
					p[bi++] = nm;
					switch (c)
					{
						case '+':
						case '-':
						{
							while (wi > 0)
							{
								p[bi++] = w[wi--];
							}
							w[++wi] = c == '+' ? CMD_ADD : CMD_SUB;
							t = TERM_EXP;
							break;
						}
						case '*':
						case '/':
						{
							while (w[wi] == '*' || w[wi] == '/')
							{
								p[bi++] = w[wi--];
							}
							w[++wi] = c == '*' ? CMD_MUL : CMD_DIV;
							t = TERM_EXP;
							break;
						}
						case '\n':
						{
							while (wi > 0)
							{
								p[bi++] = w[wi--];
							}
							p[bi++] = CMD_STORE;
							t = TERM_TOP;
							break;
						}
						default:
						{
							printf("term: %d, char '%c'\n", t, c);
							return SYNTAX_ERR;
						}
					}
				}
				break;
			}
			case TERM_LF:
			{
				if (c == '\n')
				{
					t = TERM_TOP;
				}
				else
				{
					printf("term: %d, char '%c'\n", t, c);
					return SYNTAX_ERR;
				}
				break;
			}
		}

	}
	
	switch (t)
	{
		case TERM_NUM:
		{
			p[bi++] = CMD_PUSH;
			p[bi++] = nm;
		}
		case TERM_SYB:
		{
			while (wi > 0)
			{
				p[bi++] = w[wi--];
			}
			break;
		}
		case TERM_LF:
		case TERM_TOP:
		{
			break;
		}
		default:
		{
			return SYNTAX_ERR;
		}
	}
	
	p[bi++] = CMD_END;
	
	return bi;
}

void func2(int p[], int c)
{
	int i;
	int t = 0;
	int n;
	n = p[0];
	printf("%03d VAR COUNT: %d\n", 0, n);
	for (i = 0; i < n; i++)
	{
		printf("%03d VAR AREA: %d\n", i + 1, i + 1);	
	}
	
	for (i = n + 1; i < c; i++)
	{
		if (t)
		{
			printf("%03d DATA: %d\n", i, p[i]);
			t = 0;	
		}
		else
		{
			printf("%03d CMD: %s\n", i, cmd[p[i]]);
			if (p[i] == CMD_PUSH)
			{
				t = 1;
			}
		}
	}
}

int func3(int p[], int c)
{
	int s[1000];
	int si = 0;
	int i;
	for (i = p[0] + 1; i < c; i++)
	{
		switch (p[i])
		{
			case CMD_END:
			{
				return EXST_SUCCESS;
			}
			case CMD_PUSH:
			{
				s[++si] = p[++i];
				break;
			}
			case CMD_LOAD:
			{
				s[si] = p[s[si]];
				break;
			}
			case CMD_STORE:
			{
				p[s[si - 1]] = s[si];
				si -= 2;
				break;
			}
			case CMD_ADD:
			{
				s[si - 1] += s[si];
				si--;
				break;
			}
			case CMD_SUB:
			{
				s[si - 1] -= s[si];
				si--;
				break;
			}
			case CMD_MUL:
			{
				s[si - 1] *= s[si];
				si--;
				break;
			}
			case CMD_DIV:
			{
				s[si - 1] /= s[si];
				si--;
				break;
			}
			case CMD_PRINT:
			{
				printf("%d\n", s[si--]);
				break;
			}
			default:
			{
				return EXST_RTERR;
			}
		}
	}
	return EXST_NFEND;
}
