/*
 *	jtsymbol.c
 *
 *	JoyToyインタプリタ
 *
 *	* Sun Jan 01 00:00:00 JST 2006 Naoyuki Sawa
 *	- 作成開始。
 *	* Tue Jan 31 00:00:00 JST 2006 Naoyuki Sawa
 *	- 1st リリース。
 *	* Sat Jun 17 05:26:02 JST 2006 Naoyuki Sawa
 *	- FIELD_OFFSET()→offsetof()に変更。
 */
#include "app.h"

/*****************************************************************************
 *	JTSYMBOL
 *****************************************************************************/

typedef struct _JTSYMBOL {
	int refcnt;
	JTCODE_INTERFACE* jtcode_interface;
	JTSYMBOL_INTERFACE* jtsymbol_interface;
	//
	char* name;	/* 文字列 */
} JTSYMBOL;

IMPLEMENT_JTCODE_INTERFACE(JTSYMBOL, jtsymbol)
IMPLEMENT_JTSYMBOL_INTERFACE(JTSYMBOL, jtsymbol)

const INTERFACE_MAP jtsymbol_interface_map[] = {
	{ JTCODE_INTERFACE_ID, offsetof(JTSYMBOL, jtcode_interface), &jtcode_interface },
	{ JTSYMBOL_INTERFACE_ID, offsetof(JTSYMBOL, jtsymbol_interface), &jtsymbol_interface },
	{ 0/*終端*/ },
};

/* シンボルリスト
 * インタプリタ上に存在するシンボルの数が、0→1に変化するときに、リストを作成します。
 * インタプリタ上に存在するシンボルの数が、1→0に変化するときに、リストを削除します。
 */
static LIST/*<JTSYMBOL*>*/ * symbol_list;

JTCODE_INTERFACE**
jtsymbol_intern(const char* name)
{
	JTSYMBOL* this;
	//
	int i;
	int count;

	/* シンボルリストが作成済みならば... */
	if(symbol_list) {

		/* 指定された名前に対応するシンボルが、既に存在するならば、それを返します。
		 * こうしておけば、JTSYMBOL_INTERFACEの等価判定は、ポインタ比較で行えます。
		 */
		count = list_size(symbol_list);
		if(!count) {
			DIE(); /* 有り得ない */
		}
		for(i = 0; i < count; i++) {
			this = list_get(symbol_list, i);
			if(strcmp(this->name, name) == 0) {
				return safe_addref(&this->jtcode_interface); /* 呼び出し元のために+1 */
			}
		}

	/* シンボルリストが未作成ならば... */
	} else {

		/* シンボルリストを作成します。 */
		symbol_list = list_create(&ptr_list_info);
	}

	/* 指定された名前に対応するシンボルを、新規作成します。 */
	this = calloc(1, sizeof(JTSYMBOL));
	if(!this) {
		DIE(); /* メモリ不足 */
	}
	this->refcnt = 1;
	apply_interface_map(this, jtsymbol_interface_map);

	/* 名前を複製して、格納します。 */
	this->name = strdup(name);
	if(!this->name) {
		DIE(); /* メモリ不足 */
	}

	/* シンボルリストに登録します。 */
	list_add(symbol_list, this);

	return &this->jtcode_interface;
}

static void
jtsymbol_delete(JTSYMBOL* this)
{
	int i;
	int count;

	/* 削除するシンボルを、シンボルリストから検索し... */
	if(!symbol_list) {
		DIE(); /* 有り得ない */
	}
	count = list_size(symbol_list);
	if(!count) {
		DIE(); /* 有り得ない */
	}
	for(i = 0; i < count; i++) {
		if(list_get(symbol_list, i) == this) {

			/* シンボルリストから登録解除します。 */
			list_remove(symbol_list, i);

			/* 名前を解放します。 */
			free(this->name);

			free(this);

			/* 最後の一つのシンボルだったならば、シンボルリストを削除します。 */
			if(count == 1) {
				list_delete(symbol_list);
				symbol_list = NULL; /* 忘れないで!! */
			}

			return;
		}
	}

	DIE(); /* 有り得ない */
}

/*****************************************************************************
 *	JTCODE_INTERFACE
 *****************************************************************************/

static JTCODE_INTERFACE**
jtcode_run(JTCODE_INTERFACE** intf)
{
	JTSYMBOL* this = CONTAINING_RECORD(intf, JTSYMBOL, jtcode_interface);
	//
	JTCODE_INTERFACE** code = NULL;

	/* このシンボルに対応する、変数の値を取得します。 */
	safe_attach(code, joytoy_get_symbol_value(&this->jtsymbol_interface));
	if(!code) {
		DIE(); /* 未定義 (読み込んだプログラムのエラー) */
	}

	return code;
}

/*****************************************************************************
 *	JTSYMBOL_INTERFACE
 *****************************************************************************/

static const char*
jtsymbol_get_name(JTSYMBOL_INTERFACE** intf)
{
	JTSYMBOL* this = CONTAINING_RECORD(intf, JTSYMBOL, jtsymbol_interface);

	/* 名前を返します。 */
	return this->name;
}

