/*	
 *	bltml.c
 *
 *	BulletML
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2006 Naoyuki Sawa
 *
 *	* Mon Dec 04 18:17:17 JST 2006 Naoyuki Sawa
 *	- 1st リリース。
 *	  コメントが未整備なので、このあと書く予\定です。
 *	* Tue Dec 05 00:28:23 JST 2006 Naoyuki Sawa
 *	- BulletMLBullet_new()とBulletMLBullet_delete()、BulletMLAction_new()とBulletMLAction_delete()を対で使わなければいけないことについて、コメントを追記しました。
 *	  ★重要!!★です。BulletMLBullet、BulletMLActionの実装部のコメントを参照してください。(「Tue Dec 05 00:28:23 JST 2006 Naoyuki Sawa」の日付で検索してください。)
 *	  ↓2006/12/10追記
 *	* Sun Dec 10 13:32:01 JST 2006 Naoyuki Sawa
 *	- クラス階層変更によりBulletMLBaseが無くなり、BulletMLBullet、BulletMLActionクラスは仮想デストラクタを持たないクラスとなったので、上述の問題は無くなりました。
 */
#include "clip.h"

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

static const LIST_INFO BulletML_ptrListInfo = {
	ptr_copy,	/* val_copy */
	ptr_delete,	/* val_delete */
};

static fixed
BulletML_roundDirection(fixed direction)
{
	while(ficom(direction, <, -180)) fiadd_(direction, 360);
	while(ficom(direction, >=, 180)) fisub_(direction, 360);
	return direction;
}

static int
BulletML_skipWhiteSpace(const char** pp)
{
	const char* p = *pp;
	while(isspace(*p)) { p++; }
	return *(*pp = p);
}

/****************************************************************************
 *	BulletML_load
 ****************************************************************************/

static LIST/*<BulletMLNode*>*/* BulletML_refFrom;	/* bulletRef,actionRef,fireRef */
static LIST/*<BulletMLNode*>*/* BulletML_refTo;		/* bullet,action,fire */

static BulletMLNode*
BulletML_translateElement(TiXmlElement* xmlElem)
{
	BulletMLNode* node;
	BulletMLNode* tmpNode;
	TiXmlAttribute* xmlAttr;
	TiXmlNode* xmlNode;
	TiXmlText* xmlText;
	TiXmlComment* xmlComm;
	int i;

	/* BulletMLノードを作成します。 */
	node = BulletMLNode_new(TiXmlNode_Value(TiXmlElement_super(xmlElem)));

	/* 属性を設定します。 */
	for(xmlAttr = TiXmlElement_FirstAttribute(xmlElem);
	    xmlAttr;
	    xmlAttr = TiXmlAttribute_Next(xmlAttr)) {
		if(!strcmp(TiXmlAttribute_Name(xmlAttr), "xmlns")) {	/* bulletml */
			/** no job **/
			continue; /* 継続 */
		}
		if(!strcmp(TiXmlAttribute_Name(xmlAttr), "type")) {	/* bulletml,direction,speed,horizontal,vertical */
			BulletMLNode_setType(node, TiXmlAttribute_Value(xmlAttr));
			continue; /* 継続 */
		}
		if(!strcmp(TiXmlAttribute_Name(xmlAttr), "label")) {	/* bullet,bulletRef,action,actionRef,fire,fireRef */
			BulletMLNode_setLabel(node, TiXmlAttribute_Value(xmlAttr));
			continue; /* 継続 */
		}
		DIE(); /* 不明な属性 */
	}

	/* 参照元と参照先のBulletMLノードを登録しておきます。(build()で解決します) */
	switch(BulletMLNode_getName(node)) {
	case BULLETMLNODE_NAME_BULLET:
	case BULLETMLNODE_NAME_ACTION:
	case BULLETMLNODE_NAME_FIRE:
		if(*BulletMLNode_getLabel(node)) {
			for(i = 0; i < list_size(BulletML_refTo); i++) {
				tmpNode = list_get(BulletML_refTo, i);
				if(BulletMLNode_getName(tmpNode) == BulletMLNode_getName(node) && !strcmp(BulletMLNode_getLabel(tmpNode), BulletMLNode_getLabel(node))) {
					DIE(); /* <bullet label="???">等の二重定義 */
				}
			}
			list_add(BulletML_refTo, node);
		} else {
			/* <bullet>等にはlabelが無くても良い */
		}
		break;
	case BULLETMLNODE_NAME_BULLETREF:
	case BULLETMLNODE_NAME_ACTIONREF:
	case BULLETMLNODE_NAME_FIREREF:
		if(*BulletMLNode_getLabel(node)) {
			list_add(BulletML_refFrom, node);
		} else {
			DIE(); /* <bulletRef>等にはlabel必須 */
		}
		break;
	}

	/* 子要素を処理します。 */
	for(xmlNode = TiXmlNode_FirstChild(TiXmlElement_super(xmlElem), NULL);
	    xmlNode;
	    xmlNode = TiXmlNode_NextSibling(xmlNode, NULL)) {
		if((xmlElem = TiXmlNode_ToElement(xmlNode)) != NULL) {
			BulletMLNode_addChild(node, BulletML_translateElement(xmlElem));
			continue; /* 継続 */
		}
		if((xmlText = TiXmlNode_ToText(xmlNode)) != NULL) {
			BulletMLNode_setValue(node, TiXmlNode_Value(TiXmlText_super(xmlText)));
			continue; /* 継続 */
		}
		if((xmlComm = TiXmlNode_ToComment(xmlNode)) != NULL) {
			/** no job **/
			continue; /* 継続 */
		}
		DIE();
	}

	return node;
}

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

BulletMLNode*
BulletML_load(const char* filename_or_xmltext)
{
	TiXmlDocument* xmlDoc;
	TiXmlElement* xmlElem;
	BulletMLNode* bulletml;
	int iFrom;
	int iTo;
	BulletMLNode* nodeFrom;
	BulletMLNode* nodeTo;

	/* 参照元と参照先のBulletMLノードリストを作成します。 */
	BulletML_refFrom = list_create(&BulletML_ptrListInfo);
	BulletML_refTo   = list_create(&BulletML_ptrListInfo);

	/* <bulletml>をルートとするツリーを作成します。 */
	xmlDoc = TiXmlDocument_new(NULL);
	if(strchr(filename_or_xmltext, '<')) {
		xmlDoc->vptr->Parse(xmlDoc, filename_or_xmltext);
	} else {
		TiXmlDocument_LoadFile(xmlDoc, filename_or_xmltext);
	}
	xmlElem = TiXmlNode_FirstChildElement(TiXmlDocument_super(xmlDoc), "bulletml");
	if(!xmlElem) {
		DIE();
	}
	bulletml = BulletML_translateElement(xmlElem);
	TiXmlDocument_delete(xmlDoc);

	/* 参照を解決します。 */
	for(iFrom = 0; iFrom < list_size(BulletML_refFrom); iFrom++) {
		nodeFrom = list_get(BulletML_refFrom, iFrom);
		for(iTo = 0; iTo < list_size(BulletML_refTo); iTo++) {
			nodeTo = list_get(BulletML_refTo, iTo);
			if((BulletMLNode_getName(nodeFrom) == BULLETMLNODE_NAME_BULLETREF && BulletMLNode_getName(nodeTo) == BULLETMLNODE_NAME_BULLET && !strcmp(BulletMLNode_getLabel(nodeFrom), BulletMLNode_getLabel(nodeTo))) ||
			   (BulletMLNode_getName(nodeFrom) == BULLETMLNODE_NAME_ACTIONREF && BulletMLNode_getName(nodeTo) == BULLETMLNODE_NAME_ACTION && !strcmp(BulletMLNode_getLabel(nodeFrom), BulletMLNode_getLabel(nodeTo))) ||
			   (BulletMLNode_getName(nodeFrom) == BULLETMLNODE_NAME_FIREREF   && BulletMLNode_getName(nodeTo) == BULLETMLNODE_NAME_FIRE   && !strcmp(BulletMLNode_getLabel(nodeFrom), BulletMLNode_getLabel(nodeTo)))) {
				BulletMLNode_setRef(nodeFrom, nodeTo);
				break;
			}
		}
		if(iTo == list_size(BulletML_refTo)) {
			DIE(); /* <bulletRef label="???">等に対応する<bullet label="???">等が定義されていない */
		}
	}

	/* 参照元と参照先のBulletMLノードリストを削除します。 */
	list_delete(BulletML_refFrom);
	list_delete(BulletML_refTo  );

	return bulletml;
}

/****************************************************************************
 *	BulletML_expr
 ****************************************************************************/

/*	「いまどきのプログラム言語の作り方(毎日コミュニケーションズ)」(p.106)文法6を参考にしました。
 *	昨年、この本を読んで作った、P/ECE版JoyToy(clip/joytoy)のソ\ースコードをベースに実装しました。
 *
 *	式	←	項 [('+'|'-') 項]*
 *
 *	項	←	因子 [('*'|'/'|'%') 因子]*
 *
 *	因子	←	数値
 *		｜	変数
 *		｜	'-' 因子
 *		｜	'(' 式 ')'
 *
 *	数値	←	'0〜9'+
 *		｜	'0〜9'+ '.' '0〜9'*
 *		｜	        '.' '0〜9'+
 *
 *	変数	←	'$' ('rand'|'rank'|'0〜9')
 */

static BulletMLValue* BulletML_expr(const char** pp);	/* 式 */
static BulletMLValue* BulletML_term(const char** pp);	/* 項 */
static BulletMLValue* BulletML_factor(const char** pp);	/* 因子 */

static BulletMLValue*
BulletML_expr(const char** pp)
{
	const char* p = *pp;
	int c;
	BulletMLValue* lhs;
	BulletMLValue* rhs;

	lhs = BulletML_term(&p);
	if(lhs) {
		for(;;) {
			c = BulletML_skipWhiteSpace(&p);
			if(c == '+' ||
			   c == '-') {
				p++;
				rhs = BulletML_term(&p);
				if(!rhs) {
					DIE();
				}
				lhs = BulletMLBinExpr_super(BulletMLBinExpr_new(c, lhs, rhs));
			} else {
				break;
			}
		}
	}

	*pp = p;
	return lhs;
}

static BulletMLValue*
BulletML_term(const char** pp)
{
	const char* p = *pp;
	int c;
	BulletMLValue* lhs;
	BulletMLValue* rhs;

	lhs = BulletML_factor(&p);
	if(lhs) {
		for(;;) {
			c = BulletML_skipWhiteSpace(&p);
			if(c == '*' ||
			   c == '/' ||
			   c == '%') {
				p++;
				rhs = BulletML_factor(&p);
				if(!rhs) {
					DIE();
				}
				lhs = BulletMLBinExpr_super(BulletMLBinExpr_new(c, lhs, rhs));
			} else {
				break;
			}
		}
	}

	*pp = p;
	return lhs;
}

static BulletMLValue*
BulletML_factor(const char** pp)
{
	const char* p = *pp;
	int c;
	BulletMLValue* value;

	c = BulletML_skipWhiteSpace(&p);
	if(isdigit(c) || c == '.') {
		value = BulletMLNumber_super(BulletMLNumber_new(&p));
	} else if(c == '$') {
		value = BulletMLVar_super(BulletMLVar_new(&p));
	} else if(c == '-') {
		p++;
		value = BulletML_factor(&p);
		if(!value) {
			DIE();
		}
		value = BulletMLUnExpr_super(BulletMLUnExpr_new(c, value));
	} else if(c == '(') {
		p++;
		value = BulletML_expr(&p);
		c = BulletML_skipWhiteSpace(&p);
		if(c == ')') {
			p++;
		} else {
			DIE();
		}
	} else {
		DIE();
	}

	*pp = p;
	return value;
}

/****************************************************************************
 *	BulletMLNode
 ****************************************************************************/

static const char* const BulletMLNode_nameTbl[BULLETMLNODE_NAME_COUNT] = {
	"bulletml",		/* BULLETMLNODE_NAME_BULLETML */
	"bullet",		/* BULLETMLNODE_NAME_BULLET */
	"action",		/* BULLETMLNODE_NAME_ACTION */
	"fire",			/* BULLETMLNODE_NAME_FIRE */
	"changeDirection",	/* BULLETMLNODE_NAME_CHANGEDIRECTION */
	"changeSpeed",		/* BULLETMLNODE_NAME_CHANGESPEED */
	"accel",		/* BULLETMLNODE_NAME_ACCEL */
	"wait",			/* BULLETMLNODE_NAME_WAIT */
	"vanish",		/* BULLETMLNODE_NAME_VANISH */
	"repeat",		/* BULLETMLNODE_NAME_REPEAT */
	"direction",		/* BULLETMLNODE_NAME_DIRECTION */
	"speed",		/* BULLETMLNODE_NAME_SPEED */
	"horizontal",		/* BULLETMLNODE_NAME_HORIZONTAL */
	"vertical",		/* BULLETMLNODE_NAME_VERTICAL */
	"term",			/* BULLETMLNODE_NAME_TERM */
	"times",		/* BULLETMLNODE_NAME_TIMES */
	"bulletRef",		/* BULLETMLNODE_NAME_BULLETREF */
	"actionRef",		/* BULLETMLNODE_NAME_ACTIONREF */
	"fireRef",		/* BULLETMLNODE_NAME_FIREREF */
	"param",		/* BULLETMLNODE_NAME_PARAM */
};				/* BULLETMLNODE_NAME_COUNT */

static const char* const BulletMLNode_typeTbl[BULLETMLNODE_TYPE_COUNT] = {
	"none",			/* BULLETMLNODE_TYPE_NONE */
	"vertical",		/* BULLETMLNODE_TYPE_VERTICAL */
	"horizontal",		/* BULLETMLNODE_TYPE_HORIZONTAL */
	"aim",			/* BULLETMLNODE_TYPE_AIM */
	"absolute",		/* BULLETMLNODE_TYPE_ABSOLUTE */
	"relative",		/* BULLETMLNODE_TYPE_RELATIVE */
	"sequence",		/* BULLETMLNODE_TYPE_SEQUENCE */
};				/* BULLETMLNODE_TYPE_COUNT */

static void
BulletMLNode_getParams(BulletMLNode* This/*<〜Ref>*/, fixed new_params[/*BULLETML_MAX_PARAMS*/], const fixed old_params[/*BULLETML_MAX_PARAMS*/], const BulletMLUserFunc* userFunc, void* userData)
{
	int i = 0;
	BulletMLNode* node;

	if(!new_params ||		/* 出力必須 */
	   !old_params ||		/* 入力必須 */
	   new_params == old_params) {	/* この関数はエリアスセーフな実装ではありません。(オーバーヘッド低減のため) */
		DIE();
	}
	for(node = BulletMLNode_firstChild(This);
	    node;
	    node = BulletMLNode_nextSibling(node)) {
		switch(BulletMLNode_getName(node)) {
		case BULLETMLNODE_NAME_PARAM:
			if(i >= BULLETML_MAX_PARAMS) {
				DIE();	/* 多すぎる */
			}
			new_params[i++] = node->value->vptr->calc(node->value, old_params, userFunc, userData);
			break;
		default:
			DIE();
		}
	}
	while(i < BULLETML_MAX_PARAMS) {
		new_params[i++] = 0;	/* 念のため、無効なパラメータをクリアしておくことにします。 */
	}
}

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

BulletMLNode*
BulletMLNode_new(const char* name)
{
	BulletMLNode* This = calloc(1, sizeof *This);
	if(!This) {
		DIE();
	}
	_BulletMLNode_construct(This, name);
	return This;
}

void
BulletMLNode_delete(BulletMLNode* This)
{
	if(This) {
		/** no virtual destructor **/
		_BulletMLNode_destruct(This);
		free(This);
	}
}

void
_BulletMLNode_construct(BulletMLNode* This, const char* name)
{
	/** no base class **/
	/** no vptr **/

	/* 要素名を設定します。 */
	for(This->name = 0; This->name < BULLETMLNODE_NAME_COUNT; This->name++) {
		if(!strcmp(BulletMLNode_nameTbl[This->name], name)) {
			break;
		}
	}
	if(This->name == BULLETMLNODE_NAME_COUNT) {
		DIE(); /* 不明な要素名 */
	}

	/* ラベルを空文字に初期化します。 */
	switch(This->name) {
	case BULLETMLNODE_NAME_BULLET:
	case BULLETMLNODE_NAME_BULLETREF:
	case BULLETMLNODE_NAME_ACTION:
	case BULLETMLNODE_NAME_ACTIONREF:
	case BULLETMLNODE_NAME_FIRE:
	case BULLETMLNODE_NAME_FIREREF:
		This->label = StdString_new(NULL);
		break;
	}

	/* 値はsetValue()が呼ばれるまでNULLのままとします。 */
	// This->value = NULL;
}

void
_BulletMLNode_destruct(BulletMLNode* This)
{
	/* 子ノードを全て削除します。 */
	BulletMLNode* node = This->firstChild;
	while(node) {
		BulletMLNode* next = node->nextSibling;
		BulletMLNode_delete(node);
		node = next;
	}

	/* ラベルを削除します。 */
	StdString_delete(This->label/*NULLOK*/);

	/* 値が設定されていたら削除します。 */
	BulletMLValue_delete(This->value/*NULLOK*/);

	/** no base class **/
}

int
BulletMLNode_getName(BulletMLNode* This)
{
	return This->name;
}

void
BulletMLNode_setType(BulletMLNode* This, const char* type)
{
	for(This->type = 0; This->type < BULLETMLNODE_TYPE_COUNT; This->type++) {
		if(!strcmp(BulletMLNode_typeTbl[This->type], type)) {
			break;
		}
	}
	if(This->type == BULLETMLNODE_TYPE_COUNT) {
		DIE(); /* 不明な属性値 */
	}
}

int
BulletMLNode_getType(BulletMLNode* This)
{
	return This->type;
}

void
BulletMLNode_setLabel(BulletMLNode* This, const char* label)
{
	if(!This->label) {
		DIE(); /* bullet,bulletRef,action,actionRef,fire,fireRef以外は不可 */
	}
	StdString_assign(This->label, label);
}

const char*
BulletMLNode_getLabel(BulletMLNode* This)
{
	if(!This->label) {
		DIE(); /* bullet,bulletRef,action,actionRef,fire,fireRef以外は不可 */
	}
	return StdString_c_str(This->label);
}

void
BulletMLNode_setRef(BulletMLNode* This, BulletMLNode* ref)
{
	This->ref = ref;
}

BulletMLNode*
BulletMLNode_getRef(BulletMLNode* This)
{
	if(!This->ref) {
		DIE(); /* bulletRef,actionRef,fireRef以外は不可 */
	}
	return This->ref;
}

void
BulletMLNode_setValue(BulletMLNode* This, const char* value)
{
	if(This->value) {
		DIE(); /* 二重指定 */
	}
	This->value = BulletML_expr(&value);
	if(!This->value) {
		DIE(); /* 式が不正 */
	}
	if(BulletML_skipWhiteSpace(&value)) {
		DIE(); /* 式の後にゴミが有る */
	}
}

BulletMLValue*
BulletMLNode_getValue(BulletMLNode* This)
{
	if(!This->value) {
		DIE(); /* <param>等にテキストが欠けている */
	}
	return This->value;
}

BulletMLNode*
BulletMLNode_firstChild(BulletMLNode* This)
{
	return This->firstChild;
}

BulletMLNode*
BulletMLNode_nextSibling(BulletMLNode* This)
{
	return This->nextSibling;
}

void
BulletMLNode_addChild(BulletMLNode* This, BulletMLNode* child)
{
	BulletMLNode** pp;

	pp = &This->firstChild;
	while(*pp) {
		pp = &(*pp)->nextSibling;
	}
	*pp = child;
}

BulletMLNode*
BulletMLNode_getChild(BulletMLNode* This, int name)
{
	BulletMLNode* node;

	for(node = This->firstChild;
	    node;
	    node = node->nextSibling) {
		if(node->name == name) {
			return node;
		}
	}

	return NULL;
}

void
BulletMLNode_dump(BulletMLNode* This, int indent)
{
	int i;
	BulletMLNode* node;

	for(i = 0; i < indent; i++) printf("\t");
	printf("<%s", BulletMLNode_nameTbl[This->name]);
	if(This->type) {
		printf(" type=\"%s\"", BulletMLNode_typeTbl[This->type]);
	}
	if(This->label && *StdString_c_str(This->label)) {
		printf(" label=\"%s\"", StdString_c_str(This->label));
	}
	if(This->value || This->firstChild) {
		printf(">");
		if(This->value) {
			This->value->vptr->dump(This->value);
		}
		if(This->firstChild) {
			printf("\n");
			for(node = This->firstChild;
			    node;
			    node = node->nextSibling) {
				BulletMLNode_dump(node, indent + 1);
			}
			for(i = 0; i < indent; i++) printf("\t");
		}
		printf("</%s>\n", BulletMLNode_nameTbl[This->name]);
	} else {
		printf("/>\n");
	}
}

/****************************************************************************
 *	BulletMLBullet
 ****************************************************************************/

static void
BulletMLUserFunc_createBullet(const BulletMLUserFunc* This, void* userData, fixed direction, fixed speed, BulletMLNode* bulletNode, const fixed params[/*BULLETML_MAX_PARAMS*/])
{
	TRACE("BulletMLUserFunc_createBullet(%p,%p,%f,%f,%p,[%f,%f,%f,%f,%f,%f,%f,%f,%f])\n",
		This, userData, fst(direction), fst(speed), bulletNode,
		fst(params[1-1]), fst(params[2-1]), fst(params[3-1]), fst(params[4-1]), fst(params[5-1]), fst(params[6-1]), fst(params[7-1]), fst(params[8-1]), fst(params[9-1]));
	if(This->createBullet) {
		This->createBullet(userData, direction, speed, bulletNode, params);
	}
}

static void
BulletMLUserFunc_setDirection(const BulletMLUserFunc* This, void* userData, fixed direction)
{
	TRACE("BulletMLUserFunc_setDirection(%p,%p,%f)\n", This, userData, fst(direction));
	if(This->setDirection) {
		This->setDirection(userData, BulletML_roundDirection(direction));
	}
}

static void
BulletMLUserFunc_setSpeed(const BulletMLUserFunc* This, void* userData, fixed speed)
{
	TRACE("BulletMLUserFunc_setSpeed(%p,%p,%f)\n", This, userData, fst(speed));
	if(This->setSpeed) {
		This->setSpeed(userData, speed);
	}
}

static void
BulletMLUserFunc_setAccelH(const BulletMLUserFunc* This, void* userData, fixed accelH)
{
	TRACE("BulletMLUserFunc_setAccelH(%p,%p,%f)\n", This, userData, fst(accelH));
	if(This->setAccelH) {
		This->setAccelH(userData, accelH);
	}
}

static void
BulletMLUserFunc_setAccelV(const BulletMLUserFunc* This, void* userData, fixed accelV)
{
	TRACE("BulletMLUserFunc_setAccelV(%p,%p,%f)\n", This, userData, fst(accelV));
	if(This->setAccelV) {
		This->setAccelV(userData, accelV);
	}
}

static void
BulletMLUserFunc_vanish(const BulletMLUserFunc* This, void* userData)
{
	TRACE("BulletMLUserFunc_vanish(%p,%p)\n", This, userData);
	if(This->vanish) {
		This->vanish(userData);
	}
}

static fixed
BulletMLUserFunc_getRand(const BulletMLUserFunc* This, void* userData)
{
	fixed randValue;
	if(This->getRand) {
		randValue = This->getRand(userData);
	} else {
		static int seed;
		randValue = FRND(seed);
	}
	TRACE("BulletMLUserFunc_getRand(%p,%p)=%f\n", This, userData, fst(randValue));
	return randValue;
}

static fixed
BulletMLUserFunc_getRank(const BulletMLUserFunc* This, void* userData)
{
	fixed rank;
	if(This->getRank) {
		rank = This->getRank(userData);
	} else {
		rank = 0;
	}
	TRACE("BulletMLUserFunc_getRank(%p,%p)=%f\n", This, userData, fst(rank));
	return rank;
}

static fixed
BulletMLUserFunc_getAimDirection(const BulletMLUserFunc* This, void* userData)
{
	fixed aimDirection;
	if(This->getAimDirection) {
		aimDirection = BulletML_roundDirection(This->getAimDirection(userData));
	} else {
		aimDirection = 0;
	}
	TRACE("BulletMLUserFunc_getAimDirection(%p,%p)=%f\n", This, userData, fst(aimDirection));
	return aimDirection;
}

static fixed
BulletMLUserFunc_getDirection(const BulletMLUserFunc* This, void* userData)
{
	fixed direction;
	if(This->getDirection) {
		direction = BulletML_roundDirection(This->getDirection(userData));
	} else {
		direction = 0;
	}
	TRACE("BulletMLUserFunc_getDirection(%p,%p)=%f\n", This, userData, fst(direction));
	return direction;
}

static fixed
BulletMLUserFunc_getSpeed(const BulletMLUserFunc* This, void* userData)
{
	fixed speed;
	if(This->getSpeed) {
		speed = This->getSpeed(userData);
	} else {
		speed = fild(1);
	}
	TRACE("BulletMLUserFunc_getSpeed(%p,%p)=%f\n", This, userData, fst(speed));
	return speed;
}

static fixed
BulletMLUserFunc_getAccelH(const BulletMLUserFunc* This, void* userData)
{
	fixed accelH;
	if(This->getAccelH) {
		accelH = This->getAccelH(userData);
	} else {
		accelH = 0;
	}
	TRACE("BulletMLUserFunc_getAccelH(%p,%p)=%f\n", This, userData, fst(accelH));
	return accelH;
}

static fixed
BulletMLUserFunc_getAccelV(const BulletMLUserFunc* This, void* userData)
{
	fixed accelV;
	if(This->getAccelV) {
		accelV = This->getAccelV(userData);
	} else {
		accelV = 0;
	}
	TRACE("BulletMLUserFunc_getAccelV(%p,%p)=%f\n", This, userData, fst(accelV));
	return accelV;
}

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

static void
BulletMLBullet_addAction(BulletMLBullet* This, BulletMLAction* action)
{
	BulletMLAction** pp = &This->firstAction;
	while(*pp) {
		pp = &(*pp)->nextSibling;
	}
	*pp = action;
}

static void
BulletMLBullet_changeDirection(BulletMLBullet* This, fixed direction, int term)
{
	This->direction.start = BulletMLUserFunc_getDirection(This->userFunc, This->userData);
	This->direction.diff = BulletML_roundDirection(fsub(direction, This->direction.start));
	This->direction.term = term >= 1 ? term : 1;
	This->direction.cnt = 0;
}

static void
BulletMLBullet_changeSpeed(BulletMLBullet* This, fixed speed, int term)
{
	This->speed.start = BulletMLUserFunc_getSpeed(This->userFunc, This->userData);
	This->speed.diff = fsub(speed, This->speed.start);
	This->speed.term = term >= 1 ? term : 1;
	This->speed.cnt = 0;
}

static void
BulletMLBullet_changeAccelH(BulletMLBullet* This, fixed accelH, int term)
{
	This->accelH.start = BulletMLUserFunc_getAccelH(This->userFunc, This->userData);
	This->accelH.diff = fsub(accelH, This->accelH.start);
	This->accelH.term = term >= 1 ? term : 1;
	This->accelH.cnt = 0;
}

static void
BulletMLBullet_changeAccelV(BulletMLBullet* This, fixed accelV, int term)
{
	This->accelV.start = BulletMLUserFunc_getAccelV(This->userFunc, This->userData);
	This->accelV.diff = fsub(accelV, This->accelV.start);
	This->accelV.term = term >= 1 ? term : 1;
	This->accelV.cnt = 0;
}

static fixed
BulletMLBullet_calcDirection(BulletMLBullet* This, BulletMLNode* directionNode/*NULLOK*/, const fixed params[/*BULLETML_MAX_PARAMS*/])
{
	fixed direction;
	BulletMLValue* value;

	if(directionNode) {
		value = BulletMLNode_getValue(directionNode);
		direction = value->vptr->calc(value, params, This->userFunc, This->userData);
		switch(BulletMLNode_getType(directionNode)) {
		case BULLETMLNODE_TYPE_NONE:
		case BULLETMLNODE_TYPE_AIM:
			fadd_(direction, BulletMLUserFunc_getAimDirection(This->userFunc, This->userData));
			break;
		case BULLETMLNODE_TYPE_ABSOLUTE:
			/** no job **/
			break;
		case BULLETMLNODE_TYPE_RELATIVE:
			fadd_(direction, BulletMLUserFunc_getDirection(This->userFunc, This->userData));
			break;
		case BULLETMLNODE_TYPE_SEQUENCE:
			fadd_(direction, This->direction.prev);
			break;
		default:
			DIE(); /* <direction>に適合しないtypeが指定された */
		}
	} else {
		direction = BulletMLUserFunc_getAimDirection(This->userFunc, This->userData); /* default = <direction type="aim">0</direction> */
	}

	return This->direction.prev = BulletML_roundDirection(direction);
}

static fixed
BulletMLBullet_calcSpeed(BulletMLBullet* This, BulletMLNode* speedNode/*NULLOK*/, const fixed params[/*BULLETML_MAX_PARAMS*/])
{
	fixed speed;
	BulletMLValue* value;

	if(speedNode) {
		value = BulletMLNode_getValue(speedNode);
		speed = value->vptr->calc(value, params, This->userFunc, This->userData);
		switch(BulletMLNode_getType(speedNode)) {
		case BULLETMLNODE_TYPE_NONE:
		case BULLETMLNODE_TYPE_ABSOLUTE:
			/** no job **/
			break;
		case BULLETMLNODE_TYPE_RELATIVE:
			fadd_(speed, BulletMLUserFunc_getSpeed(This->userFunc, This->userData));
			break;
		case BULLETMLNODE_TYPE_SEQUENCE:
			fadd_(speed, This->speed.prev);
			break;
		default:
			DIE(); /* <speed>に適合しないtypeが指定された */
		}
	} else {
		speed = fild(1); /* default = <speed type="absolute">1</speed> */
	}

	return This->speed.prev = speed;
}

static fixed
BulletMLBullet_calcAccelH(BulletMLBullet* This, BulletMLNode* horizontalNode, const fixed params[/*BULLETML_MAX_PARAMS*/])
{
	fixed accelH;
	BulletMLValue* value;

	value = BulletMLNode_getValue(horizontalNode);
	accelH = value->vptr->calc(value, params, This->userFunc, This->userData);
	switch(BulletMLNode_getType(horizontalNode)) {
	case BULLETMLNODE_TYPE_NONE:
	case BULLETMLNODE_TYPE_ABSOLUTE:
		/** no job **/
		break;
	case BULLETMLNODE_TYPE_RELATIVE:
		fadd_(accelH, BulletMLUserFunc_getAccelH(This->userFunc, This->userData));
		break;
	case BULLETMLNODE_TYPE_SEQUENCE:
		fadd_(accelH, This->accelH.prev);
		break;
	default:
		DIE(); /* <horizontal>に適合しないtypeが指定された */
	}

	return This->accelH.prev = accelH;
}

static fixed
BulletMLBullet_calcAccelV(BulletMLBullet* This, BulletMLNode* verticalNode, const fixed params[/*BULLETML_MAX_PARAMS*/])
{
	fixed accelV;
	BulletMLValue* value;

	value = BulletMLNode_getValue(verticalNode);
	accelV = value->vptr->calc(value, params, This->userFunc, This->userData);
	switch(BulletMLNode_getType(verticalNode)) {
	case BULLETMLNODE_TYPE_NONE:
	case BULLETMLNODE_TYPE_ABSOLUTE:
		/** no job **/
		break;
	case BULLETMLNODE_TYPE_RELATIVE:
		fadd_(accelV, BulletMLUserFunc_getAccelV(This->userFunc, This->userData));
		break;
	case BULLETMLNODE_TYPE_SEQUENCE:
		fadd_(accelV, This->accelV.prev);
		break;
	default:
		DIE(); /* <vertical>に適合しないtypeが指定された */
	}

	return This->accelV.prev = accelV;
}

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

BulletMLBullet*
BulletMLBullet_new(const BulletMLUserFunc* userFunc, void* userData, BulletMLNode* bulletNode, const fixed params[/*BULLETML_MAX_PARAMS*/]/*NULL*/)
{
	/* * Mon Dec 04 17:07:30 JST 2006 Naoyuki Sawa
	 * - リアルタイムでひんぱんに割り当て/開放が繰り返されるのは、BulletMLBulletとBulletMLActionの二種類の構\造体だけです。
	 *   従って、これら二種類の構\造体をプーリングし、malloc()/free()のオーバーヘッドを回避します。
	 * - それ以外のBulletMLNode構\造体などは、ロード、アンロード時だけ割り当て/開放を行うので、あまり性能\が要求されません。
	 *   従って、BulletMLNode構\造体などはプーリングを行いません。
	 * * Tue Dec 05 00:28:23 JST 2006 Naoyuki Sawa
	 * - ★重要!!★BulletMLBullet_new()で作成したオブジェクトは、必ず、BulletMLBullet_delete()を使って削除してください。
	 *   BulletMLBullet_new()で作成したオブジェクトを、基底クラスのBulletMLBase_delete()を使って削除することは、本来、問題の無い使用方法です。
	 *   しかしこの場合は、pool_alloc()とpool_free()の対応が取れなくなってしまうため、プーリングが正しく動作しなくなってしまいます。
	 *   BulletMLBullet_new()で作成したオブジェクトを、BulletMLBase_delete()で削除してはいけません。必ず、BulletMLBullet_delete()で削除してください。
	 *   同じ理由により、もしも今後BulletMLBulletの派生クラスを定義した場合に、派生クラスオブジェクトをBulletMLBullet_delete()で削除しないでください。
	 *   ↓2006/12/10追記
	 * * Sun Dec 10 13:32:01 JST 2006 Naoyuki Sawa
	 * - クラス階層変更によりBulletMLBaseが無くなり、BulletMLBulletクラスは仮想デストラクタを持たないクラスとなったので、上述の問題は無くなりました。
	 */
	static POOL pool = { sizeof(BulletMLBullet) }; /* STATICです!! */
	BulletMLBullet* This = pool_alloc(&pool); /* オーバーヘッド低減のため、プールから確保 */
	_BulletMLBullet_construct(This, userFunc, userData, bulletNode, params);
	return This;
}

void
BulletMLBullet_delete(BulletMLBullet* This)
{
	if(This) {
		/** no virtual destructor **/
		_BulletMLBullet_destruct(This);
		pool_free(This); /* オーバーヘッド低減のため、プールに返す */
	}
}

void
_BulletMLBullet_construct(BulletMLBullet* This, const BulletMLUserFunc* userFunc, void* userData, BulletMLNode* bulletNode, const fixed params[/*BULLETML_MAX_PARAMS*/]/*NULL*/)
{
	/** no base class **/
	/** no vptr **/

	BulletMLNode* node;
	fixed d;

	This->userFunc = userFunc;
	This->userData = userData;

	switch(BulletMLNode_getName(bulletNode)) {
	case BULLETMLNODE_NAME_BULLETML:
		if(params) {
			DIE(); /* アプリケーショントリガによる能\動的なトップレベルBulletMLBullet_new()はパラメータ指定不可 */
		}
		for(node = BulletMLNode_firstChild(bulletNode);
		    node;
		    node = BulletMLNode_nextSibling(node)) {
			switch(BulletMLNode_getName(node)) {
			case BULLETMLNODE_NAME_BULLET:
				/** no job **/
				break;
			case BULLETMLNODE_NAME_ACTION:
				if(!strncmp(BulletMLNode_getLabel(node), "top", 3)) {
					BulletMLBullet_addAction(This, BulletMLAction_new(This, node, This->params));
				} else {
					/** no job **/
				}
				break;
			case BULLETMLNODE_NAME_FIRE:
				/** no job **/
				break;
			default:
				DIE();
			}
		}
		break;
	case BULLETMLNODE_NAME_BULLET:
	case BULLETMLNODE_NAME_BULLETREF:
		if(!params) {
			DIE(); /* BulletMLUserFunc.createBullet()に応答した受動的なBulletMLBullet_new()はパラメータ指定必須 */
		}
		if(BulletMLNode_getName(bulletNode) == BULLETMLNODE_NAME_BULLET) {
			memcpy(This->params, params, sizeof(fixed) * BULLETML_MAX_PARAMS);
		} else {
			BulletMLNode_getParams(bulletNode, This->params, params, userFunc, userData);
			bulletNode = BulletMLNode_getRef(bulletNode);
		}
		for(node = BulletMLNode_firstChild(bulletNode);
		    node;
		    node = BulletMLNode_nextSibling(node)) {
			switch(BulletMLNode_getName(node)) {
			case BULLETMLNODE_NAME_DIRECTION:
				d = BulletMLBullet_calcDirection(This, node, This->params);
				BulletMLUserFunc_setDirection(userFunc, userData, d);
				break;
			case BULLETMLNODE_NAME_SPEED:
				d = BulletMLBullet_calcSpeed(This, node, This->params);
				BulletMLUserFunc_setSpeed(userFunc, userData, d);
				break;
			case BULLETMLNODE_NAME_ACTION:
			case BULLETMLNODE_NAME_ACTIONREF:
				BulletMLBullet_addAction(This, BulletMLAction_new(This, node, This->params));
				break;
			default:
				DIE();
			}
		}
		break;
	default:
		DIE();
	}
}

void
_BulletMLBullet_destruct(BulletMLBullet* This)
{
	BulletMLAction* action = This->firstAction;
	while(action) {
		BulletMLAction* next = action->nextSibling;
		BulletMLAction_delete(action);
		action = next;
	}

	/** no base class **/
}

const BulletMLUserFunc*
BulletMLBullet_getUserFunc(BulletMLBullet* This)
{
	return This->userFunc;
}

void*
BulletMLBullet_getUserData(BulletMLBullet* This)
{
	return This->userData;
}

int
BulletMLBullet_run(BulletMLBullet* This)
{
	int active = 0;
	BulletMLAction* action;
	fixed d;

	action = This->firstAction;
	while(action) {
		if(BulletMLAction_run(action)) {
			active = 1;
		}
		action = action->nextSibling;
	}

	if(This->direction.cnt < This->direction.term) {
		This->direction.cnt++;
		d = fadd(This->direction.start, fidiv(fimul(This->direction.diff, This->direction.cnt), This->direction.term));
		BulletMLUserFunc_setDirection(This->userFunc, This->userData, d);
		active = 1;
	}
	if(This->speed.cnt < This->speed.term) {
		This->speed.cnt++;
		d = fadd(This->speed.start, fidiv(fimul(This->speed.diff, This->speed.cnt), This->speed.term));
		BulletMLUserFunc_setSpeed(This->userFunc, This->userData, d);
		active = 1;
	}
	if(This->accelH.cnt < This->accelH.term) {
		This->accelH.cnt++;
		d = fadd(This->accelH.start, fidiv(fimul(This->accelH.diff, This->accelH.cnt), This->accelH.term));
		BulletMLUserFunc_setAccelH(This->userFunc, This->userData, d);
		active = 1;
	}
	if(This->accelV.cnt < This->accelV.term) {
		This->accelV.cnt++;
		d = fadd(This->accelV.start, fidiv(fimul(This->accelV.diff, This->accelV.cnt), This->accelV.term));
		BulletMLUserFunc_setAccelV(This->userFunc, This->userData, d);
		active = 1;
	}

	return active;
}

/****************************************************************************
 *	BulletMLAction
 ****************************************************************************/

BulletMLAction*
BulletMLAction_new(BulletMLBullet* bullet, BulletMLNode* actionNode, const fixed params[/*BULLETML_MAX_PARAMS*/])
{
	/* * Mon Dec 04 17:07:30 JST 2006 Naoyuki Sawa
	 * - リアルタイムでひんぱんに割り当て/開放が繰り返されるのは、BulletMLBulletとBulletMLActionの二種類の構\造体だけです。
	 *   従って、これら二種類の構\造体をプーリングし、malloc()/free()のオーバーヘッドを回避します。
	 * - それ以外のBulletMLNode構\造体などは、ロード、アンロード時だけ割り当て/開放を行うので、あまり性能\が要求されません。
	 *   従って、BulletMLNode構\造体などはプーリングを行いません。
	 * * Tue Dec 05 00:28:23 JST 2006 Naoyuki Sawa
	 * - ★重要!!★BulletMLAction_new()で作成したオブジェクトは、必ず、BulletMLAction_delete()を使って削除してください。
	 *   BulletMLAction_new()で作成したオブジェクトを、基底クラスのBulletMLBase_delete()を使って削除することは、本来、問題の無い使用方法です。
	 *   しかしこの場合は、pool_alloc()とpool_free()の対応が取れなくなってしまうため、プーリングが正しく動作しなくなってしまいます。
	 *   BulletMLAction_new()で作成したオブジェクトを、BulletMLBase_delete()で削除してはいけません。必ず、BulletMLAction_delete()で削除してください。
	 *   同じ理由により、もしも今後BulletMLActionの派生クラスを定義した場合に、派生クラスオブジェクトをBulletMLAction_delete()で削除しないでください。
	 *   ↓2006/12/10追記
	 * * Sun Dec 10 13:32:01 JST 2006 Naoyuki Sawa
	 * - クラス階層変更によりBulletMLBaseが無くなり、BulletMLActionクラスは仮想デストラクタを持たないクラスとなったので、上述の問題は無くなりました。
	 */
	static POOL pool = { sizeof(BulletMLAction) }; /* STATICです!! */
	BulletMLAction* This = pool_alloc(&pool); /* オーバーヘッド低減のため、プールから確保 */
	_BulletMLAction_construct(This, bullet, actionNode, params);
	return This;
}

void
BulletMLAction_delete(BulletMLAction* This)
{
	if(This) {
		/** no virtual destructor **/
		_BulletMLAction_destruct(This);
		pool_free(This); /* オーバーヘッド低減のため、プールに返す */
	}
}

void
_BulletMLAction_construct(BulletMLAction* This, BulletMLBullet* bullet, BulletMLNode* actionNode, const fixed params[/*BULLETML_MAX_PARAMS*/])
{
	/** no base class **/
	/** no vptr **/

	switch(BulletMLNode_getName(actionNode)) {
	case BULLETMLNODE_NAME_ACTION:
		memcpy(This->params, params, sizeof(fixed) * BULLETML_MAX_PARAMS);
		break;
	case BULLETMLNODE_NAME_ACTIONREF:
		BulletMLNode_getParams(actionNode, This->params, params, BulletMLBullet_getUserFunc(/*×This->*/bullet), BulletMLBullet_getUserData(/*×This->*/bullet));
		actionNode = BulletMLNode_getRef(actionNode);
		break;
	default:
		DIE();
	}

	This->bullet = bullet;
	This->pc = BulletMLNode_firstChild(actionNode);
}

void
_BulletMLAction_destruct(BulletMLAction* This)
{
	BulletMLAction_delete(This->childAction/*NULLOK*/);

	/** no base class **/
}

int
BulletMLAction_run(BulletMLAction* This)
{
	BulletMLValue* value;
	BulletMLNode* node;
	BulletMLNode* fireNode;
	fixed fireParams[BULLETML_MAX_PARAMS];
	fixed direction;
	fixed speed;
	fixed accelH;
	fixed accelV;
	int term;

	for(;;) {
		if(This->childAction) {
			if(BulletMLAction_run(This->childAction)) {
				return 1;
			} else {
				BulletMLAction_delete(This->childAction);
				This->childAction = NULL;
			}
		}
		if(This->wait > 0) {
			This->wait--;
			return 1;
		}
		if(!This->pc) {
			return 0;
		}
		switch(BulletMLNode_getName(This->pc)) {
		case BULLETMLNODE_NAME_REPEAT:
			if(This->repeat > 0) {
				This->repeat--;
			} else {
				node = BulletMLNode_getChild(This->pc, BULLETMLNODE_NAME_TIMES);
				if(!node) {
					DIE(); /* <repeat>の子要素に<times>が無い */
				}
				value = BulletMLNode_getValue(node);
				This->repeat = fist(value->vptr->calc(value, This->params, BulletMLBullet_getUserFunc(This->bullet), BulletMLBullet_getUserData(This->bullet)));
			}
			if(This->repeat > 0) {
				node = BulletMLNode_getChild(This->pc, BULLETMLNODE_NAME_ACTION);
				if(!node) {
					node = BulletMLNode_getChild(This->pc, BULLETMLNODE_NAME_ACTIONREF);
					if(!node) {
						DIE(); /* <repeat>の子要素に<action>または<actionRef>が無い */
					}
				}
				This->childAction = BulletMLAction_new(This->bullet, node, This->params);
			} else {
				This->pc = BulletMLNode_nextSibling(This->pc);
			}
			break;
		case BULLETMLNODE_NAME_FIRE:
		case BULLETMLNODE_NAME_FIREREF:
			if(BulletMLNode_getName(This->pc) == BULLETMLNODE_NAME_FIRE) {
				memcpy(fireParams, This->params, sizeof(fixed) * BULLETML_MAX_PARAMS);
				fireNode = This->pc;
			} else {
				BulletMLNode_getParams(This->pc, fireParams, This->params, BulletMLBullet_getUserFunc(This->bullet), BulletMLBullet_getUserData(This->bullet));
				fireNode = BulletMLNode_getRef(This->pc);
			}
			/*{{direction*/
			node = BulletMLNode_getChild(fireNode, BULLETMLNODE_NAME_DIRECTION);
			direction = BulletMLBullet_calcDirection(This->bullet, node/*NULLOK*/, fireParams);
			/*}}direction*/
			/*{{speed*/
			node = BulletMLNode_getChild(fireNode, BULLETMLNODE_NAME_SPEED);
			speed = BulletMLBullet_calcSpeed(This->bullet, node/*NULLOK*/, fireParams);
			/*}}speed*/
			/*{{bullet|bulletRef*/
			node = BulletMLNode_getChild(fireNode, BULLETMLNODE_NAME_BULLET);
			if(!node) {
				node = BulletMLNode_getChild(fireNode, BULLETMLNODE_NAME_BULLETREF);
				if(!node) {
					DIE(); /* <fire>の子要素に<bullet>または<bulletRef>が無い */
				}
			}
			BulletMLUserFunc_createBullet(BulletMLBullet_getUserFunc(This->bullet), BulletMLBullet_getUserData(This->bullet), direction, speed, node, fireParams);
			/*}}bullet|bulletRef*/
			This->pc = BulletMLNode_nextSibling(This->pc);
			break;
		case BULLETMLNODE_NAME_CHANGEDIRECTION:
			node = BulletMLNode_getChild(This->pc, BULLETMLNODE_NAME_TERM);
			if(!node) {
				DIE(); /* <changeDirection>の子要素に<term>が無い */
			}
			value = BulletMLNode_getValue(node);
			term = fist(value->vptr->calc(value, This->params, BulletMLBullet_getUserFunc(This->bullet), BulletMLBullet_getUserData(This->bullet)));
			node = BulletMLNode_getChild(This->pc, BULLETMLNODE_NAME_DIRECTION);
			if(!node) {
				DIE(); /* <changeDirection>の子要素に<direction>が無い */
			}
			direction = BulletMLBullet_calcDirection(This->bullet, node, This->params);
			BulletMLBullet_changeDirection(This->bullet, direction, term);
			This->pc = BulletMLNode_nextSibling(This->pc);
			break;
		case BULLETMLNODE_NAME_CHANGESPEED:
			node = BulletMLNode_getChild(This->pc, BULLETMLNODE_NAME_TERM);
			if(!node) {
				DIE(); /* <changeSpeed>の子要素に<term>が無い */
			}
			value = BulletMLNode_getValue(node);
			term = fist(value->vptr->calc(value, This->params, BulletMLBullet_getUserFunc(This->bullet), BulletMLBullet_getUserData(This->bullet)));
			node = BulletMLNode_getChild(This->pc, BULLETMLNODE_NAME_SPEED);
			if(!node) {
				DIE(); /* <changeSpeed>の子要素に<speed>が無い */
			}
			speed = BulletMLBullet_calcSpeed(This->bullet, node, This->params);
			BulletMLBullet_changeSpeed(This->bullet, speed, term);
			This->pc = BulletMLNode_nextSibling(This->pc);
			break;
		case BULLETMLNODE_NAME_ACCEL:
			node = BulletMLNode_getChild(This->pc, BULLETMLNODE_NAME_TERM);
			if(!node) {
				DIE(); /* <accel>の子要素に<term>が無い */
			}
			value = BulletMLNode_getValue(node);
			term = fist(value->vptr->calc(value, This->params, BulletMLBullet_getUserFunc(This->bullet), BulletMLBullet_getUserData(This->bullet)));
			node = BulletMLNode_getChild(This->pc, BULLETMLNODE_NAME_HORIZONTAL);
			if(node) {
				accelH = BulletMLBullet_calcAccelH(This->bullet, node, This->params);
				BulletMLBullet_changeAccelH(This->bullet, accelH, term);
			}
			node = BulletMLNode_getChild(This->pc, BULLETMLNODE_NAME_VERTICAL);
			if(node) {
				accelV = BulletMLBullet_calcAccelV(This->bullet, node, This->params);
				BulletMLBullet_changeAccelV(This->bullet, accelV, term);
			}
			This->pc = BulletMLNode_nextSibling(This->pc);
			break;
		case BULLETMLNODE_NAME_WAIT:
			value = BulletMLNode_getValue(This->pc);
			This->wait = fist(value->vptr->calc(value, This->params, BulletMLBullet_getUserFunc(This->bullet), BulletMLBullet_getUserData(This->bullet)));
			This->pc = BulletMLNode_nextSibling(This->pc);
			break;
		case BULLETMLNODE_NAME_VANISH:
			BulletMLUserFunc_vanish(BulletMLBullet_getUserFunc(This->bullet), BulletMLBullet_getUserData(This->bullet));
			This->pc = BulletMLNode_nextSibling(This->pc);
			break;
		case BULLETMLNODE_NAME_ACTION:
		case BULLETMLNODE_NAME_ACTIONREF:
			This->childAction = BulletMLAction_new(This->bullet, This->pc, This->params);
			This->pc = BulletMLNode_nextSibling(This->pc);
			break;
		default:
			DIE();
		}
	}
}

/****************************************************************************
 *	BulletMLValue
 ****************************************************************************/

const BulletMLValue_vtbl _BulletMLValue_vtbl = {
	_BulletMLValue_destruct,	/*destruct*/
	NULL,				/*calc*/
	NULL,				/*dump*/
	/*↑BulletMLValue_vtbl↑*/
};

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

void
BulletMLValue_delete(BulletMLValue* This)
{
	if(This) {
		This->vptr->destruct(This);
		free(This);
	}
}

void
_BulletMLValue_construct(BulletMLValue* This)
{
	/** no base class **/
	This->vptr = &_BulletMLValue_vtbl;
}

/*virtual*/ void
_BulletMLValue_destruct(BulletMLValue* This)
{
	/** no base class **/
}

/****************************************************************************
 *	BulletMLNumber
 ****************************************************************************/

const BulletMLNumber_vtbl _BulletMLNumber_vtbl = {
	_BulletMLNumber_destruct,	/*destruct*/
	_BulletMLNumber_calc,		/*calc*/
	_BulletMLNumber_dump,		/*dump*/
	/*↑BulletMLValue_vtbl↑*/
	/*↑BulletMLNumber_vtbl↑*/
};

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

BulletMLNumber*
BulletMLNumber_new(const char** pp)
{
	BulletMLNumber* This = calloc(1, sizeof *This);
	if(!This) {
		DIE();
	}
	_BulletMLNumber_construct(This, pp);
	return This;
}

void
BulletMLNumber_delete(BulletMLNumber* This)
{
	if(This) {
		This->vptr->destruct(This);
		free(This);
	}
}

void
_BulletMLNumber_construct(BulletMLNumber* This, const char** pp)
{
	_BulletMLValue_construct(BulletMLNumber_super(This));
	This->vptr = &_BulletMLNumber_vtbl;

	This->value = fld(strtod(*pp, (char**)pp));
}

/*virtual*/ void
_BulletMLNumber_destruct(BulletMLNumber* This)
{
	_BulletMLValue_destruct(BulletMLNumber_super(This));
}

/*virtual*/ fixed
_BulletMLNumber_calc(BulletMLNumber* This, const fixed params[/*BULLETML_MAX_PARAMS*/], const BulletMLUserFunc* userFunc, void* userData)
{
	return This->value;
}

/*virtual*/ void
_BulletMLNumber_dump(BulletMLNumber* This)
{
	printf("%f", fst(This->value));
}

/****************************************************************************
 *	BulletMLVar
 ****************************************************************************/

const BulletMLVar_vtbl _BulletMLVar_vtbl = {
	_BulletMLVar_destruct,	/*destruct*/
	_BulletMLVar_calc,		/*calc*/
	_BulletMLVar_dump,		/*dump*/
	/*↑BulletMLValue_vtbl↑*/
	/*↑BulletMLVar_vtbl↑*/
};

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

BulletMLVar*
BulletMLVar_new(const char** pp)
{
	BulletMLVar* This = calloc(1, sizeof *This);
	if(!This) {
		DIE();
	}
	_BulletMLVar_construct(This, pp);
	return This;
}

void
BulletMLVar_delete(BulletMLVar* This)
{
	if(This) {
		This->vptr->destruct(This);
		free(This);
	}
}

void
_BulletMLVar_construct(BulletMLVar* This, const char** pp)
{
	_BulletMLValue_construct(BulletMLVar_super(This));
	This->vptr = &_BulletMLVar_vtbl;

	{
		const char* p = *pp;
		if(*p != '$') {
			DIE();
		}
		p++;
		if(*p >= '1' && *p <= '9') {
			This->id = *p - '1';
			p++;
		} else if(!strncmp(p, "rand", 4)) {
			This->id = -1;
			p += 4;
		} else if(!strncmp(p, "rank", 4)) {
			This->id = -2;
			p += 4;
		} else {
			DIE();
		}
		*pp = p;
	}
}

/*virtual*/ void
_BulletMLVar_destruct(BulletMLVar* This)
{
	_BulletMLValue_destruct(BulletMLVar_super(This));
}

/*virtual*/ fixed
_BulletMLVar_calc(BulletMLVar* This, const fixed params[/*BULLETML_MAX_PARAMS*/], const BulletMLUserFunc* userFunc, void* userData)
{
	switch(This->id) {
	case -1:
		return BulletMLUserFunc_getRand(userFunc, userData);
	case -2:
		return BulletMLUserFunc_getRank(userFunc, userData);
	default:
		return params[This->id];
	}
}

/*virtual*/ void
_BulletMLVar_dump(BulletMLVar* This)
{
	switch(This->id) {
	case -1:
		printf("$rand");
		break;
	case -2:
		printf("$rank");
		break;
	default:
		printf("$%c", This->id + '1');
		break;
	}
}

/****************************************************************************
 *	BulletMLUnExpr
 ****************************************************************************/

const BulletMLUnExpr_vtbl _BulletMLUnExpr_vtbl = {
	_BulletMLUnExpr_destruct,	/*destruct*/
	_BulletMLUnExpr_calc,		/*calc*/
	_BulletMLUnExpr_dump,		/*dump*/
	/*↑BulletMLValue_vtbl↑*/
	/*↑BulletMLUnExpr_vtbl↑*/
};

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

BulletMLUnExpr*
BulletMLUnExpr_new(int op, BulletMLValue* value)
{
	BulletMLUnExpr* This = calloc(1, sizeof *This);
	if(!This) {
		DIE();
	}
	_BulletMLUnExpr_construct(This, op, value);
	return This;
}

void
BulletMLUnExpr_delete(BulletMLUnExpr* This)
{
	if(This) {
		This->vptr->destruct(This);
		free(This);
	}
}

void
_BulletMLUnExpr_construct(BulletMLUnExpr* This, int op, BulletMLValue* value)
{
	_BulletMLValue_construct(BulletMLUnExpr_super(This));
	This->vptr = &_BulletMLUnExpr_vtbl;

	This->op = op;
	This->value = value;
}

/*virtual*/ void
_BulletMLUnExpr_destruct(BulletMLUnExpr* This)
{
	BulletMLValue_delete(This->value);

	_BulletMLValue_destruct(BulletMLUnExpr_super(This));
}

/*virtual*/ fixed
_BulletMLUnExpr_calc(BulletMLUnExpr* This, const fixed params[/*BULLETML_MAX_PARAMS*/], const BulletMLUserFunc* userFunc, void* userData)
{
	fixed value = This->value->vptr->calc(This->value, params, userFunc, userData);

	switch(This->op) {
	case '-':
		return fchs(value);
	default:
		DIE();
	}
}

/*virtual*/ void
_BulletMLUnExpr_dump(BulletMLUnExpr* This)
{
	printf("%c", This->op);
	This->value->vptr->dump(This->value);
}

/****************************************************************************
 *	BulletMLBinExpr
 ****************************************************************************/

const BulletMLBinExpr_vtbl _BulletMLBinExpr_vtbl = {
	_BulletMLBinExpr_destruct,	/*destruct*/
	_BulletMLBinExpr_calc,		/*calc*/
	_BulletMLBinExpr_dump,		/*dump*/
	/*↑BulletMLValue_vtbl↑*/
	/*↑BulletMLBinExpr_vtbl↑*/
};

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

BulletMLBinExpr*
BulletMLBinExpr_new(int op, BulletMLValue* lhs, BulletMLValue* rhs)
{
	BulletMLBinExpr* This = calloc(1, sizeof *This);
	if(!This) {
		DIE();
	}
	_BulletMLBinExpr_construct(This, op, lhs, rhs);
	return This;
}

void
BulletMLBinExpr_delete(BulletMLBinExpr* This)
{
	if(This) {
		This->vptr->destruct(This);
		free(This);
	}
}

void
_BulletMLBinExpr_construct(BulletMLBinExpr* This, int op, BulletMLValue* lhs, BulletMLValue* rhs)
{
	_BulletMLValue_construct(BulletMLBinExpr_super(This));
	This->vptr = &_BulletMLBinExpr_vtbl;

	This->op = op;
	This->lhs = lhs;
	This->rhs = rhs;
}

/*virtual*/ void
_BulletMLBinExpr_destruct(BulletMLBinExpr* This)
{
	BulletMLValue_delete(This->lhs);
	BulletMLValue_delete(This->rhs);

	_BulletMLValue_destruct(BulletMLBinExpr_super(This));
}

/*virtual*/ fixed
_BulletMLBinExpr_calc(BulletMLBinExpr* This, const fixed params[/*BULLETML_MAX_PARAMS*/], const BulletMLUserFunc* userFunc, void* userData)
{
	fixed lhs = This->lhs->vptr->calc(This->lhs, params, userFunc, userData);
	fixed rhs = This->rhs->vptr->calc(This->rhs, params, userFunc, userData);

	switch(This->op) {
	case '+':
		return fadd(lhs, rhs);
	case '-':
		return fsub(lhs, rhs);
	case '*':
		return fmul(lhs, rhs);
	case '/':
		return fdiv(lhs, rhs);
	case '%':
		return fprem(lhs, rhs);
	default:
		DIE();
	}
}

/*virtual*/ void
_BulletMLBinExpr_dump(BulletMLBinExpr* This)
{
	printf("(");
	This->lhs->vptr->dump(This->lhs);
	printf("%c", This->op);
	This->rhs->vptr->dump(This->rhs);
	printf(")");
}

