/*
 *	jtfun.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"

/*****************************************************************************
 *	JTFUN
 *****************************************************************************/

typedef struct _JTFUN {
	int refcnt;
	JTCODE_INTERFACE* jtcode_interface;
	JTFUN_INTERFACE* jtfun_interface;
	//
	JTSYMBOL_INTERFACE** name;			/* 関数名を表すシンボル */
	LIST/*<JTSYMBOL_INTERFACE**>*/ * params_list;	/* 仮引数リスト */
	JTCODE_INTERFACE** body;			/* 関数の本体 */
	int method;					/* 通常の関数ならば0、メソッドならば0以外の値 */
} JTFUN;

IMPLEMENT_JTCODE_INTERFACE(JTFUN, jtfun)
IMPLEMENT_JTFUN_INTERFACE(JTFUN, jtfun)

const INTERFACE_MAP jtfun_interface_map[] = {
	{ JTCODE_INTERFACE_ID, offsetof(JTFUN, jtcode_interface), &jtcode_interface },
	{ JTFUN_INTERFACE_ID, offsetof(JTFUN, jtfun_interface), &jtfun_interface },
	{ 0/*終端*/ },
};

JTCODE_INTERFACE**
jtfun_create(JTSYMBOL_INTERFACE** name, LIST/*<JTSYMBOL_INTERFACE**>*/ * params_list, JTCODE_INTERFACE** body, int method)
{
	JTFUN* this = calloc(1, sizeof(JTFUN));
	if(!this) {
		DIE(); /* メモリ不足 */
	}
	this->refcnt = 1;
	apply_interface_map(this, jtfun_interface_map);

	/* 関数名を表すシンボルを格納します。 */
	safe_assign(this->name, name);

	/* 仮引数リストを複製して、格納します。 */
	this->params_list = list_duplicate(params_list);

	/* 関数の本体を格納します。 */
	safe_assign(this->body, body);

	/* 通常の関数、または、メソッドの種別を格納します。 */
	this->method = method;

	return &this->jtcode_interface;
}

static void
jtfun_delete(JTFUN* this)
{
	/* 関数名を表すシンボルを解放します。 */
	safe_release(this->name);

	/* 仮引数リストを複製して、解放します。 */
	list_delete(this->params_list);

	/* 関数の本体を解放します。 */
	safe_release(this->body);

	free(this);
}

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

static JTCODE_INTERFACE**
jtcode_run(JTCODE_INTERFACE** intf)
{
	JTFUN* this = CONTAINING_RECORD(intf, JTFUN, jtcode_interface);

	/* ローカル変数テーブル、または、グローバル変数テーブルに、関数を登録します。 */
	joytoy_def_symbol_value(this->name, intf);

	return safe_addref(intf); /* 呼び出し元のために+1 */
}

/*****************************************************************************
 *	JTFUN_INTERFACE
 *****************************************************************************/

static JTSYMBOL_INTERFACE**
jtfun_get_name(JTFUN_INTERFACE** intf)
{
	JTFUN* this = CONTAINING_RECORD(intf, JTFUN, jtfun_interface);

	/* 関数名を表すシンボルを返します。 */
	return safe_addref(this->name); /* 呼び出し元のために+1 */
}

static JTCODE_INTERFACE**
jtfun_call(JTFUN_INTERFACE** intf, LIST/*<JTCODE_INTERFACE**>*/ * args_list, JTOBJECT_INTERFACE** object)
{
	JTFUN* this = CONTAINING_RECORD(intf, JTFUN, jtfun_interface);
	//
	JTSYMBOL_INTERFACE** symbol = NULL;
	JTCODE_INTERFACE** code = NULL;
	//
	int i;
	int argc;

	/* 関数フレームに入ります。 */
	joytoy_enter();

	/* 仮引数と実引数の数が同じであることを、確認します。 */
	argc = list_size(args_list);
	if(list_size(this->params_list) != argc) {
		DIE(); /* 仮引数と実引数の数が違う */
	}

	/* 仮引数のシンボルと、実引数の値を、ローカル変数テーブルに登録します。 */
	for(i = 0; i < argc; i++) {
		safe_attach(symbol, list_get(this->params_list, i));
		safe_attach(code, list_get(args_list, i));
		joytoy_def_symbol_value(symbol, code);
	}

	/* メソッドならば、実行対象オブジェクトを設定します。 */
	if(this->method) {
		if(!object) {
			DIE(); /* オブジェクトが指定されていない */
		}
		joytoy_set_object(object);
	}

	/* 関数の本体を実行します。 */
	safe_attach(code, (*this->body)->run(this->body));

	/* 関数フレームを抜けます。 */
	joytoy_leave();

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

	return code;
}

