/*
 *	joytoy.c
 *
 *	JoyToyインタプリタ
 *
 *	* Sun Jan 01 00:00:00 JST 2006 Naoyuki Sawa
 *	- 作成開始。
 *	* Tue Jan 31 00:00:00 JST 2006 Naoyuki Sawa
 *	- 1st リリース。
 */
#include "app.h"

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

const LIST_INFO ptr_list_info = {
	/* val = (void*) */
	ptr_copy,	/* 複製 */
	ptr_delete,	/* 削除 */
};

const LIST_INFO jtcode_list_info = {
	/* val = (JTCODE_INTERFACE**) */
	unk_copy,	/* 複製 */
	unk_delete,	/* 削除 */
};

const LIST_INFO jtsymbol_list_info = {
	/* val = (JTSYMBOL_INTERFACE**) */
	unk_copy,	/* 複製 */
	unk_delete,	/* 削除 */
};

const MAP_INFO str_to_int_map_info = {
	/* key = (char*) */
	str_copy,	/* 複製 */
	str_delete,	/* 削除 */
	str_equals,	/* 比較 */
	/* value = (int) */
	ptr_copy,	/* 複製 */
	ptr_delete,	/* 削除 */
};

const MAP_INFO jtsymbol_to_jtcode_map_info = {
	/* key = (JTSYMBOL_INTERFACE**) */
	unk_copy,	/* 複製 */
	unk_delete,	/* 削除 */
	unk_equals,	/* 比較 */
	/* value = (JTCODE_INTERFACE**) */
	unk_copy,	/* 複製 */
	unk_delete,	/* 削除 */
};

/*****************************************************************************
 *	JOYTOY
 *****************************************************************************/

typedef struct _JOYTOY {
	MAP/*<char*,int>*/ * reserved_table;					/* 予約語テーブル */
	MAP/*<JTSYMBOL_INTERFACE**,JTCODE_INTERFACE**>*/ * global_table;	/* グローバル変数テーブル */
	LIST/*<MAP*>*/ * local_table_stack;					/* ローカル変数テーブルのスタック */
} JOYTOY;

static JOYTOY* joytoy;

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

static void
init_reserved_table()
{
	static const struct {
		const char* name;	/* 予約語 */
		int type;		/* トークンタイプ */
	} name_type[] = {
		{ "true", TOKEN_TYPE_TRUE },
		{ "false", TOKEN_TYPE_FALSE },
		{ "if", TOKEN_TYPE_IF },
		{ "else", TOKEN_TYPE_ELSE },
		{ "while", TOKEN_TYPE_WHILE },
		{ "fun", TOKEN_TYPE_FUN },
		{ "var", TOKEN_TYPE_VAR },
		{ "object", TOKEN_TYPE_OBJECT },
		{ "new", TOKEN_TYPE_NEW },
		/* TODO: 予約語が増えたら、ここに追加してください。 */
	};
	int i;

	/* 予約語テーブルを作成します。 */
	joytoy->reserved_table = map_create(&str_to_int_map_info);

	/* 予約語を登録します。 */
	for(i = 0; i < ARRAY_SIZE(name_type); i++) {
		map_put(joytoy->reserved_table, (void*)name_type[i].name, (void*)name_type[i].type);
	}
}

static void
free_reserved_table()
{
	/* 予約語テーブルを削除します。 */
	map_delete(joytoy->reserved_table);
}

static void
init_global_table()
{
	/* グローバル変数テーブルを作成します。 */
	joytoy->global_table = map_create(&jtsymbol_to_jtcode_map_info);
}

static void
free_global_table()
{
	/* グローバル変数テーブルを削除します。 */
	map_delete(joytoy->global_table);
}

static void
init_local_table_stack()
{
	/* ローカル変数テーブルのスタックを作成します。 */
	joytoy->local_table_stack = list_create(&ptr_list_info);
}

static void
free_local_table_stack()
{
	/* ローカル変数テーブルが残っていたら、削除します。
	 * 関数の中で終了したことを意味し、通常、有り得ないはずですが、
	 * 将来、終了を行う組み込み関数を追加した場合に備えて、こうしておきます。
	 */
	while(list_size(joytoy->local_table_stack)) {
		joytoy_leave();
	}

	/* ローカル変数テーブルのスタックを削除します。 */
	list_delete(joytoy->local_table_stack);
}

static void
prim_init()
{
	/* 組み込み関数を登録します。 */
	JTCODE_INTERFACE** prim[] = {
		jtprim_print_create(),
		jtprim_max_create(),
		/* TODO: 組み込み関数が増えたら、ここに追加してください。 */
	};
	int i;
	for(i = 0; i < ARRAY_SIZE(prim); i++) {
		safe_attach(prim[i], (*prim[i])->run(prim[i])); /* 登録 */
		safe_release(prim[i]);
	}
}

static void
joytoy_init()
{
	joytoy = calloc(1, sizeof(JOYTOY));
	if(!joytoy) {
		DIE(); /* メモリ不足 */
	}

	/* 予約語テーブルを作成します。 */
	init_reserved_table();

	/* グローバル変数テーブルを作成します。 */
	init_global_table();

	/* ローカル変数テーブルのスタックを作成します。 */
	init_local_table_stack();

	/* 組み込み関数を登録します。 */
	prim_init();
}

static void
joytoy_exit()
{
	/* 予約語テーブルを削除します。 */
	free_reserved_table();

	/* グローバル変数テーブルを削除します。 */
	free_global_table();

	/* ローカル変数テーブルのスタックを削除します。 */
	free_local_table_stack();

	free(joytoy);
}

/* グローバル変数の値を取得します。
 * グローバル変数が定義されていなければ、NULLを返します。
 */
static JTCODE_INTERFACE**
joytoy_get_global_value(JTSYMBOL_INTERFACE** symbol)
{
	JTCODE_INTERFACE** value = NULL;

	/* グローバル変数が定義されていたら... */
	if(map_contains(joytoy->global_table, symbol)) {

		/* グローバル変数の値を取得します。 */
		safe_attach(value, map_get(joytoy->global_table, symbol));
	}

	return value;
}

/* グローバル変数の値を設定します。
 * グローバル変数が定義されていなければ、新しく定義します。
 */
static void
joytoy_set_global_value(JTSYMBOL_INTERFACE** symbol, JTCODE_INTERFACE** value)
{
	/* グローバル変数の値を設定します。 */
	map_put(joytoy->global_table, symbol, value);
}

/* ローカル変数の値を取得します。
 * ローカル変数が定義されていなければ、NULLを返します。
 */
static JTCODE_INTERFACE**
joytoy_get_local_value(JTSYMBOL_INTERFACE** symbol)
{
	MAP/*<JTSYMBOL_INTERFACE**,JTCODE_INTERFACE**>*/ * local_table;
	//
	JTCODE_INTERFACE** value = NULL;

	/* 関数の中ならば... */
	if(list_size(joytoy->local_table_stack)) {
		local_table = list_get(joytoy->local_table_stack, 0);

		/* ローカル変数が定義されていたら... */
		if(map_contains(local_table, symbol)) {

			/* ローカル変数の値を取得します。 */
			safe_attach(value, map_get(local_table, symbol));
		}
	}

	return value;
}

/* ローカル変数の値を設定します。
 * ローカル変数が定義されていなければ、新しく定義します。
 */
static void
joytoy_set_local_value(JTSYMBOL_INTERFACE** symbol, JTCODE_INTERFACE** value)
{
	MAP/*<JTSYMBOL_INTERFACE**,JTCODE_INTERFACE**>*/ * local_table;

	/* ローカル変数の値を設定します。 */
	if(!list_size(joytoy->local_table_stack)) {
		DIE(); /* 関数の中でない */
	}
	local_table = list_get(joytoy->local_table_stack, 0);
	map_put(local_table, symbol, value);
}

/* フィールド変数の値を設定します。
 * フィールド変数が定義されていなければ、新しく定義します。
 */
static JTCODE_INTERFACE**
joytoy_get_field_value(JTSYMBOL_INTERFACE** symbol)
{
	JTCODE_INTERFACE** value = NULL;
	JTOBJECT_INTERFACE** object = NULL;

	/* メソッドの中ならば... */
	safe_attach(object, joytoy_get_object());
	if(object) {

		/* フィールド変数の値、または、NULL(未定義)を取得します。 */
		safe_attach(value, (*object)->get_field_value(object, symbol));
	}

	/* 不要になったインターフェイスを解放します。 */
	safe_release(object);

	return value;
}

static void
joytoy_set_field_value(JTSYMBOL_INTERFACE** symbol, JTCODE_INTERFACE** value)
{
	JTOBJECT_INTERFACE** object = NULL;

	/* フィールド変数の値を設定します。 */
	safe_attach(object, joytoy_get_object());
	if(!object) {
		DIE(); /* メソッドの中でない */
	}
	(*object)->set_field_value(object, symbol, value);

	/* 不要になったインターフェイスを解放します。 */
	safe_release(object);
}

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

void
joytoy_main(FILE* in)
{
	LEXER_INTERFACE** lexer = NULL;
	PARSER_INTERFACE** parser = NULL;
	JTCODE_INTERFACE** code = NULL;

	/* JoyToyを作成します。 */
	joytoy_init();

	/* Lexer、Parserを作成します。 */
	safe_attach(lexer, lexer_create(in));
	safe_attach(parser, parser_create(lexer));

	for(;;) {
#ifdef WIN32
		/* キーボードからの入力ならば、プロンプトを表示します。 */
		if(_isatty(_fileno(in))) {
			printf("JoyToy: ");
		}
#endif /*WIN32*/
		/* プログラムを読み込み、解決します。 */
		safe_attach(code, (*parser)->parse(parser));
		if(!code) {
			break; /* プログラム終了 */
		}
		safe_attach(code, (*code)->run(code));
#if 0
		/* 解決した値を、表示します。 */
		printf("result = ");
		joytoy_print(code);
		printf("\n");
#endif
	}

	/* 不要になったインターフェイスを解放します。 */
	safe_release(lexer);
	safe_release(parser);
	safe_release(code);

	/* JoyToyを解放します。 */
	joytoy_exit();
}

void
joytoy_print(JTCODE_INTERFACE** code)
{
	JTINT_INTERFACE** jtint = NULL;
	JTSTRING_INTERFACE** jtstring = NULL;
	JTBOOL_INTERFACE** jtbool = NULL;

	/* NULL */
	if(!code) {
		printf("NULL");
		goto L_EXIT;
	}

	/* 数値 */
	safe_assign_interface(jtint, code, JTINT_INTERFACE);
	if(jtint) {
		printf("%d", (*jtint)->get_value(jtint));
		goto L_EXIT;
	}

	/* 文字列 */
	safe_assign_interface(jtstring, code, JTSTRING_INTERFACE);
	if(jtstring) {
		printf("\"%s\"", (*jtstring)->get_string(jtstring));
		goto L_EXIT;
	}

	/* 真偽値 */
	safe_assign_interface(jtbool, code, JTBOOL_INTERFACE);
	if(jtbool) {
		printf("%s", (*jtbool)->get_value(jtbool) ? "true" : "false");
		goto L_EXIT;
	}

	/* TODO: 表示可能なコードが増えたら、ここに追加してください。 */

	DIE(); /* 不明なコード */

L_EXIT:
	/* 不要になったインターフェイスを解放します。 */
	safe_release(jtint);
	safe_release(jtstring);
	safe_release(jtbool);
}

int
joytoy_get_reserved(const char* name)
{
	/* 予約語テーブルを検索します。 */
	if(map_contains(joytoy->reserved_table, (void*)name)) {
		return (int)map_get(joytoy->reserved_table, (void*)name);
	}

	return -1; /* 予約語が登録されていない */
}

void
joytoy_enter()
{
	MAP/*<JTSYMBOL_INTERFACE**,JTCODE_INTERFACE**>*/ * local_table;

	/* ローカル変数テーブルを作成します。 */
	local_table = map_create(&jtsymbol_to_jtcode_map_info);

	/* ローカル変数テーブルを、スタックの先頭に追加します。 */
	list_insert(joytoy->local_table_stack, 0, local_table);
}

void
joytoy_leave()
{
	MAP/*<JTSYMBOL_INTERFACE**,JTCODE_INTERFACE**>*/ * local_table;

	/* スタックの先頭に有る、ローカル変数テーブルを取得します。 */
	if(!list_size(joytoy->local_table_stack)) {
		DIE(); /* ローカル変数テーブルが無い */
	}
	local_table = list_get(joytoy->local_table_stack, 0);

	/* フレームスタックのトップを削除します。 */
	list_remove(joytoy->local_table_stack, 0);

	/* ローカル変数テーブルを削除します。 */
	map_delete(local_table);
}

JTOBJECT_INTERFACE**
joytoy_get_object()
{
	JTCODE_INTERFACE** code = NULL;
	JTSYMBOL_INTERFACE** symbol = NULL;
	JTOBJECT_INTERFACE** object = NULL;

	/* ローカル変数meの値、または、NULL(未定義)を取得します。 */
	safe_attach(code, jtsymbol_intern("me"));
	safe_assign_interface(symbol, code, JTSYMBOL_INTERFACE);
	if(!symbol) {
		DIE(); /* 有り得ない */
	}
	safe_attach(code, joytoy_get_local_value(symbol));
	safe_assign_interface(object, code, JTOBJECT_INTERFACE);

	/* 不要になったインターフェイスを解放します。 */
	safe_release(code);
	safe_release(symbol);

	return object;
}

void
joytoy_set_object(JTOBJECT_INTERFACE** object)
{
	JTCODE_INTERFACE** code = NULL;
	JTSYMBOL_INTERFACE** symbol = NULL;

	/* ローカル変数meに、実行対象のオブジェクトを登録します。 */
	safe_attach(code, jtsymbol_intern("me"));
	safe_assign_interface(symbol, code, JTSYMBOL_INTERFACE);
	if(!symbol) {
		DIE(); /* 有り得ない */
	}
	safe_assign_interface(code, object, JTCODE_INTERFACE);
	if(!code) {
		DIE(); /* 有り得ない */
	}
	joytoy_set_local_value(symbol, code);

	/* 不要になったインターフェイスを解放します。 */
	safe_release(code);
	safe_release(symbol);
}

JTCODE_INTERFACE**
joytoy_get_symbol_value(JTSYMBOL_INTERFACE** symbol)
{
	JTCODE_INTERFACE** value = NULL;

	/* ローカル変数が定義されていたら、ローカル変数の値を取得します。 */
	safe_attach(value, joytoy_get_local_value(symbol));
	if(value) {
		goto L_EXIT;
	}

	/* フィールド変数が定義されていたら、フィールド変数の値を取得します。 */
	safe_attach(value, joytoy_get_field_value(symbol));
	if(value) {
		goto L_EXIT;
	}

	/* グローバル変数が定義されていたら、グローバル変数の値を取得します。 */
	safe_attach(value, joytoy_get_global_value(symbol));
	if(value) {
		goto L_EXIT;
	}

L_EXIT:
	return value;
}

void
joytoy_set_symbol_value(JTSYMBOL_INTERFACE** symbol, JTCODE_INTERFACE** value)
{
	JTCODE_INTERFACE** code = NULL;

	/* ローカル変数が定義されていたら、ローカル変数の値を設定します。 */
	safe_attach(code, joytoy_get_local_value(symbol));
	if(code) {
		joytoy_set_local_value(symbol, value);
		goto L_EXIT;
	}

	/* フィールド変数が定義されていたら、フィールド変数の値を設定します。 */
	safe_attach(code, joytoy_get_field_value(symbol));
	if(code) {
		joytoy_set_field_value(symbol, value);
		goto L_EXIT;
	}

	/* グローバル変数が定義されていたら、グローバル変数の値を設定します。 */
	safe_attach(code, joytoy_get_global_value(symbol));
	if(code) {
		joytoy_set_global_value(symbol, value);
		goto L_EXIT;
	}

	DIE(); /* 変数が定義されていない */

L_EXIT:
	/* 不要になったインターフェイスを解放します。 */
	safe_release(code);
}

void
joytoy_def_symbol_value(JTSYMBOL_INTERFACE** symbol, JTCODE_INTERFACE** value)
{
	MAP/*<JTSYMBOL_INTERFACE**,JTCODE_INTERFACE**>*/ * table;

	/* 関数の中ならば、新しいローカル変数を定義します。
	 * 関数の中でなければ、新しいグローバル変数を定義します。
	 */
	if(list_size(joytoy->local_table_stack)) {
		table = list_get(joytoy->local_table_stack, 0);
	} else {
		table = joytoy->global_table;
	}
	if(map_contains(table, symbol)) {
		DIE(); /* 二重定義 */
	}
	map_put(table, symbol, value);
}

