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

/*****************************************************************************
 *	JTBINEXPR
 *****************************************************************************/

typedef struct _JTBINEXPR {
	int refcnt;
	JTCODE_INTERFACE* jtcode_interface;
	//
	int op;				/* 演算子 */
	JTCODE_INTERFACE** code1;	/* 左辺値 */
	JTCODE_INTERFACE** code2;	/* 右辺値 */
} JTBINEXPR;

IMPLEMENT_JTCODE_INTERFACE(JTBINEXPR, jtbinexpr)

const INTERFACE_MAP jtbinexpr_interface_map[] = {
	{ JTCODE_INTERFACE_ID, offsetof(JTBINEXPR, jtcode_interface), &jtcode_interface },
	{ 0/*終端*/ },
};

JTCODE_INTERFACE**
jtbinexpr_create(int op, JTCODE_INTERFACE** code1, JTCODE_INTERFACE** code2)
{
	JTBINEXPR* this = calloc(1, sizeof(JTBINEXPR));
	if(!this) {
		DIE(); /* メモリ不足 */
	}
	this->refcnt = 1;
	apply_interface_map(this, jtbinexpr_interface_map);

	/* 演算子、左辺値、右辺値を格納します。 */
	this->op = op;
	safe_assign(this->code1, code1);
	safe_assign(this->code2, code2);

	return &this->jtcode_interface;
}

static void
jtbinexpr_delete(JTBINEXPR* this)
{
	/* 左辺値、右辺値を解放します。 */
	safe_release(this->code1);
	safe_release(this->code2);

	free(this);
}

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

static JTCODE_INTERFACE**
jtcode_run(JTCODE_INTERFACE** intf)
{
	JTBINEXPR* this = CONTAINING_RECORD(intf, JTBINEXPR, jtcode_interface);
	//
	JTCODE_INTERFACE** code1 = NULL;
	JTCODE_INTERFACE** code2 = NULL;
	ARITHMETIC_INTERFACE** arithmetic = NULL;
	LOGICAL_INTERFACE** logical = NULL;
	COMPARE_INTERFACE** compare = NULL;
	JTSYMBOL_INTERFACE** symbol = NULL;
	//
	int retval;

	switch(this->op) {
	case '+':
	case '-':
	case '*':
	case '/':
		/* 左辺値、右辺値を解決します。 */
		safe_attach(code1, (*this->code1)->run(this->code1));
		safe_attach(code2, (*this->code2)->run(this->code2));

		/* 左辺値の算術演算インターフェイスを取得します。 */
		safe_assign_interface(arithmetic, code1, ARITHMETIC_INTERFACE);
		if(!arithmetic) {
			DIE(); /* 算術演算不可 */
		}

		/* 演算子によって... */
		switch(this->op) {
		case '+':
			safe_attach(code1, (*arithmetic)->add(arithmetic, code2));
			break;
		case '-':
			safe_attach(code1, (*arithmetic)->sub(arithmetic, code2));
			break;
		case '*':
			safe_attach(code1, (*arithmetic)->mul(arithmetic, code2));
			break;
		case '/':
			safe_attach(code1, (*arithmetic)->div(arithmetic, code2));
			break;
		default:
			DIE(); /* 不明な演算子 */
		}

		goto L_EXIT;

	case TOKEN_TYPE_OR:
	case TOKEN_TYPE_AND:
		/* 左辺値、右辺値を解決します。 */
		safe_attach(code1, (*this->code1)->run(this->code1));
		safe_attach(code2, (*this->code2)->run(this->code2));

		/* 左辺値の論理演算インターフェイスを取得します。 */
		safe_assign_interface(logical, code1, LOGICAL_INTERFACE);
		if(!logical) {
			DIE(); /* 算術演算不可 */
		}

		/* 演算子によって... */
		switch(this->op) {
		case TOKEN_TYPE_OR:
			safe_attach(code1, (*logical)->or(logical, code2));
			break;
		case TOKEN_TYPE_AND:
			safe_attach(code1, (*logical)->and(logical, code2));
			break;
		default:
			DIE(); /* 不明な演算子 */
		}

		goto L_EXIT;

	case TOKEN_TYPE_EQ:
	case TOKEN_TYPE_NE:
	case TOKEN_TYPE_LE:
	case TOKEN_TYPE_GE:
	case '<':
	case '>':
		/* 左辺値、右辺値を解決します。 */
		safe_attach(code1, (*this->code1)->run(this->code1));
		safe_attach(code2, (*this->code2)->run(this->code2));

		/* 左辺値の比較演算インターフェイスを取得します。 */
		safe_assign_interface(compare, code1, COMPARE_INTERFACE);
		if(!compare) {
			DIE(); /* 比較演算不可 */
		}

		/* 左辺値と右辺値の比較結果を取得します。 */
		retval = (*compare)->compare(compare, code2);

		/* 演算子によって... */
		switch(this->op) {
		case TOKEN_TYPE_EQ:
			safe_attach(code1, jtbool_create(retval == 0));
			break;
		case TOKEN_TYPE_NE:
			safe_attach(code1, jtbool_create(retval != 0));
			break;
		case TOKEN_TYPE_LE:
			safe_attach(code1, jtbool_create(retval <= 0));
			break;
		case TOKEN_TYPE_GE:
			safe_attach(code1, jtbool_create(retval >= 0));
			break;
		case '<':
			safe_attach(code1, jtbool_create(retval < 0));
			break;
		case '>':
			safe_attach(code1, jtbool_create(retval > 0));
			break;
		default:
			DIE(); /* 不明な演算子 */
		}

		goto L_EXIT;

	case '=':
		/* 右辺値を解決します。 */
		safe_attach(code1, (*this->code2)->run(this->code2));

		/* 左辺値のシンボルインターフェイスを取得します。 */
		safe_assign_interface(symbol, this->code1, JTSYMBOL_INTERFACE);
		if(!symbol) {
			DIE(); /* 左辺値がシンボルでない */
		}

		/* ローカル変数、または、フィールド変数、または、グローバル変数の値を設定します。 */
		joytoy_set_symbol_value(symbol, code1);

		/* 代入した値code1が、そのまま、戻り値になります。 */

		goto L_EXIT;

	default:
		DIE(); /* 不明な演算子 */
	}
L_EXIT:

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

	return code1;
}

