スクリプト読み込みツール

NekoCannonのスクリプトを読み込む処理を作ってみました。
 
ただ、独自の実装のため、微妙に動作が異なりますが…。
 
例えば、

{key1=value1,key2=value2,key3=value3},

とすると、key3の値を読み込みません…。
 
このように、value3の後ろに「,」をつけると読み込めます。

{key1=value1,key2=value2,key3=value3,},

 
以下、自作の読み込み処理です。

/**
 * NekoCannonスクリプト読み込みツール
 */

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <string>

using namespace std;

struct Node
{
	typedef vector<Node> NodeList;
	typedef map<string, NodeList> ChildMap;
	typedef pair<string, NodeList> ChildPair;
	string   value;  // 値
	ChildMap childs; // 子ノードたち
};

// ============================================================================
// 半角スペース・タブを削除
// @param  変換する文字列
// @return 空文字の場合falseを返す
// ============================================================================
bool lstrip(string &str)
{
	if(str.length() == 0) return false;

	char c = str.at(0);
	while(c == ' ' || c == '\t')
	{
		str = str.erase(0, 1);
		c = str.at(0);
	}
	if(str.length() == 0) return false;
	return true;
}

// ============================================================================
// テキスト読み込みの再帰関数
// @param ifs  ファイルオブジェクト
// @param line 行の文字列
// @param node ノード
// ============================================================================
void reflexLoad(ifstream &ifs, string &line, Node &node)
{
	string key = "DUMMY";
	string dlmType = "";
	while(!ifs.eof())
	{
		// 下準備
		if(!lstrip(line))
		{
			// 空行
			char tmp[256];
			ifs.getline(tmp, 256);
			line = tmp;
			continue;
		}
		size_t nEq   = line.find('=');
		size_t nList = line.find('[');
		size_t eList = line.find(']');
		size_t nMap  = line.find('{');
		size_t eMap  = line.find('}');
		size_t nDlm  = line.find(',');

		if(nEq == -1 && nList == -1 && eList == -1 && nMap == -1 && eMap == -1 && nDlm == -1)
		{
			// 空行
			char tmp[256];
			ifs.getline(tmp, 256);
			line = tmp;
			continue;
		}
		else
		{
			// 先頭区切り文字を取得
			map<char*, size_t> idxMap;
			idxMap.insert(pair<char*, size_t>("nEq", nEq));
			idxMap.insert(pair<char*, size_t>("nList", nList));
			idxMap.insert(pair<char*, size_t>("eList", eList));
			idxMap.insert(pair<char*, size_t>("nMap", nMap));
			idxMap.insert(pair<char*, size_t>("eMap", eMap));
			idxMap.insert(pair<char*, size_t>("nDlm", nDlm));
			int nMin = 256;
			for(map<char*, size_t>::iterator iter = idxMap.begin(); iter != idxMap.end(); ++iter)
			{
				if(iter->second == -1) continue;
				if(iter->second < nMin)
				{
					nMin = iter->second;
					dlmType = iter->first;
				}
			}
		}

		// 解析開始
		if(dlmType.compare("nEq") == 0)
		{
			// 『=』
			key = line.substr(0, nEq);
			line = line.substr(nEq+1);
			continue;
		}
		else if(dlmType.compare("nList") == 0)
		{
			// 『[』
			if(node.childs.count(key) == 0)
			{
				// ノードリスト作成
				Node::NodeList nodeList;
				node.childs.insert(Node::ChildPair(key, nodeList));
			}
			line = line.substr(nList+1);
			Node childNode;
			reflexLoad(ifs, line, childNode);
			node.childs.find(key)->second.push_back(childNode);
			continue;
		}
		else if(dlmType.compare("eList") == 0)
		{
			// 『]』
			line = line.substr(eList+1);
			return;
		}
		else if(dlmType.compare("nMap") == 0)
		{
			// 『{』
			line = line.substr(nMap+1);
			reflexLoad(ifs, line, node);
			continue;
		}
		else if(dlmType.compare("eMap") == 0)
		{
			// 『}』
			line = line.substr(eMap+1);
			return;
		}
		else if(dlmType.compare("nDlm") == 0)
		{
			// 『,』
			if(node.childs.count(key) == 0)
			{
				// ノードリスト作成
				Node::NodeList nodeList;
				node.childs.insert(Node::ChildPair(key, nodeList));
			}

			Node childNode;
			childNode.value = line.substr(0, nDlm);
			line = line.substr(nDlm+1);
			if(childNode.value.length() != 0)
			{
				node.childs.find(key)->second.push_back(childNode);
			}
			continue;
		}
		else
		{
			throw "Syntax Error";
		}
	}
	
}

// ============================================================================
// ノードをデバッグ出力
// @param node ノード
// ============================================================================
int g_rank = 0;
void DebugPrint(Node &node)
{
	g_rank++;
	cout << "rank ->" << g_rank << endl;
	cout << "value->" << node.value.c_str() << endl;
	Node::ChildMap::iterator iter = node.childs.begin();
	while(iter != node.childs.end())
	{
		cout << "key  ->" << iter->first.c_str() << endl;
		Node::NodeList list = iter->second;
		for(unsigned int i = 0; i < list.size(); i++)
		{
			DebugPrint(list.at(i));
		}
		iter++;
	}
	cout << "-----------" << endl;
	g_rank--;
}

// ============================================================================
// テストドライバ
// ============================================================================
void main()
{
	ifstream ifs("data.txt");
	if(ifs.fail())
	{
		return;
	}
	char tmp[256];
	ifs.getline(tmp, 256); // 一行目は空読みしてね♪
	ifs.getline(tmp, 256);
	string line(tmp);

	Node node; // こいつに全部はいるよ
	reflexLoad(ifs, line, node); // 解析開始

	ifs.close();

	cout << "---result---" << endl;
	DebugPrint(node);

	// 使い方♪
	cout << "type     ->" << node.childs.find("type")->second.at(0).value << endl;
	cout << "max_enemy->" << node.childs.find("max_enemy")->second.at(0).value << endl;
	cout << "posx     ->" << node.childs.find("posx")->second.at(0).value << endl;
	cout << "scale    ->" << node.childs.find("scale")->second.at(0).value << endl;
	cout << "enemies  ->" << endl;
	cout << "  name :" << node.childs.find("enemies")->second.at(0).childs.find("name")->second.at(0).value << endl;
	cout << "  x    :" << node.childs.find("enemies")->second.at(0).childs.find("x")->second.at(0).value << endl;
	cout << "  life :" << node.childs.find("enemies")->second.at(0).childs.find("life")->second.at(0).value << endl;
	cout << "  scale:" << node.childs.find("enemies")->second.at(0).childs.find("scale")->second.at(0).value << endl;
	cout << "messages ->" << endl;
	Node::NodeList list = node.childs.find("messages")->second.at(0).childs.find("DUMMY")->second;
	for(unsigned int i = 0; i < list.size(); i++)
	{
		cout << "  " << list.at(i).value << endl;
	}
}

 
こいつ(data.txt)を読み込むと、

{
	type=0,
	max_enemy=3,

	posx=5,

	scale=30,

	enemies=[
		{name=Bubble,x=23,life=1,scale=6,},
	],

	messages=[
		"kenmoはこのプログラムを書くのに、",
		"まるまる2日もかかってしまいましたとさ",
		"…もうちょっと、構文解析の勉強をしないと、",
		"ダメですね。",
	],

}

 
こんな感じで出力されます。

---result---
rank ->1
value->
key  ->enemies
rank ->2
value->
key  ->DUMMY
key  ->life
rank ->3
value->1
-----------
key  ->name
rank ->3
value->Bubble
-----------
key  ->scale
rank ->3
value->6
-----------
key  ->x
rank ->3
value->23
-----------
-----------
key  ->max_enemy
rank ->2
value->3
-----------
key  ->messages
rank ->2
value->
key  ->DUMMY
rank ->3
value->"kenmoはこのプログラムを書くのに、"
-----------
rank ->3
value->"まるまる2日もかかってしまいましたとさ"
-----------
rank ->3
value->"…もうちょっと、構文解析の勉強をしないと、"
-----------
rank ->3
value->"ダメですね。"
-----------
-----------
key  ->posx
rank ->2
value->5
-----------
key  ->scale
rank ->2
value->30
-----------
key  ->type
rank ->2
value->0
-----------
-----------
type     ->0
max_enemy->3
posx     ->5
scale    ->30
enemies  ->
  name :Bubble
  x    :23
  life :1
  scale:6
messages ->
  "kenmoはこのプログラムを書くのに、"
  "まるまる2日もかかってしまいましたとさ"
  "…もうちょっと、構文解析の勉強をしないと、"
  "ダメですね。"

リストとマップが自由にネストできるので、
なかなか応用が利くのではないかと、思います。