BoostのSpiritに関する質問です。
Spiritの機能を1つのクラスにまとめ、最終的には“1つの文字列を受け取り、それを解析し、独自のルールに従って区切り、区切ったものをメンバ変数に保持”したいと思っています。
具体的には、
typedef class CScriptParser
{
public:
// parse_info
boost::spirit::parse_info<> parseInfo;
// 構文解析時のトークン保存用
std::string name_buffer;
struct Sparser : public boost::spirit::grammar<Sparser>
{
template<typename S> struct definition {
typedef boost::spirit::rule<S> rule_t;
rule_t A;
// 構文解析ルールの記述
definition(const Sparser& self)
{
A = boost::spirit::alpha_p;//[&func];
}
const rule_t& start() const { return A; }
};
};
Sparser parser;
} CScriptParser;
こういう様なクラスを作りたいと思っています(もちろん、後々に拡張していきますが)。
セマンティックアクション(上のクラスで言えば、func)を考えなければ問題なかったのですが、セマンティックアクションが絡んできた途端に分からなくなってしまいました。
どうにか、CScriptParser内のname_bufferに解析した値を持たせることはできないでしょうか。
(グローバル変数にて実装するようなやり方では出来たのですが、グローバル変数を用いない方法はないでしょうか)。
よろしくお願いします。
オリジナルを残してあまり弄らないように書いたつもりですが、
例えばこんな方法(参照で渡しておく)とか。
(なお、Spirit1.8ではなくSpirit2.0/classicで試してますので、
ヘッダ名やnamespace等は適宜置き換えてください。
Boost.Spiritは2.xになると1.xとはなんか別物…って感じです。
classicは2.xにおける1.x系の名残)
#include <boost/spirit/include/classic.hpp>
#include <boost/spirit/include/classic_parser.hpp>
#include <boost/spirit/include/classic_actor.hpp>
class CScriptParser
{
public:
// parse_info
boost::spirit::classic::parse_info<> parseInfo;
// 構文解析時のトークン保存用
std::string name_buffer;
struct Sparser : public boost::spirit::classic::grammar<Sparser>
{
template<typename S> struct definition {
typedef boost::spirit::classic::rule<S> rule_t;
rule_t A;
// 構文解析ルールの記述
definition(const Sparser& self)
{
A = boost::spirit::classic::alpha_p[boost::spirit::classic::assign_a(self.name_buffer_)];
}
const rule_t& start() const { return A; }
};
explicit Sparser(std::string& name_buffer) : name_buffer_(name_buffer) {}
std::string& name_buffer_;
};
Sparser parser;
explicit CScriptParser() : parser(name_buffer)
{
// test
parseInfo = boost::spirit::classic::parse("a", parser, boost::spirit::classic::space_p);
}
};
ご返信ありがとうございます。
こういう描き方もあるんですね。
勉強になります……。
しかし、Sparserが
Sparser parser;
という形でグローバル変数があるのが気になります。。。
どうにかならないものでしょうか。
> という形でグローバル変数があるのが気になります。。。
Sparser parser;はCScriptParserのメンバ変数であって、
グローバル変数ではありませんが、見間違えていますか?
class CScriptParser
{
public:
// parse_info
boost::spirit::classic::parse_info<> parseInfo;
// 構文解析時のトークン保存用
std::string name_buffer;
struct Sparser : public boost::spirit::classic::grammar<Sparser>
{
template<typename S> struct definition {
typedef boost::spirit::classic::rule<S> rule_t;
rule_t A;
// 構文解析ルールの記述
definition(const Sparser& self)
{
A = boost::spirit::classic::alpha_p[boost::spirit::classic::assign_a(self.name_buffer_)];
}
const rule_t& start() const { return A; }
};
explicit Sparser(std::string& name_buffer) : name_buffer_(name_buffer) {}
std::string& name_buffer_;
};
Sparser parser;
explicit CScriptParser() : parser(name_buffer)
{
// test
parseInfo = boost::spirit::classic::parse("a", parser, boost::spirit::classic::space_p);
}
};
何を望んでるのか、よく分からなくなりましたが、
「メンバ変数」と「グローバル変数」の違いは理解されていますか。
参照渡しやSemantic Actionとは無関係な話でしたし、Sparser parser;は
「グローバル変数ではない」ので、
> (グローバル変数にて実装するようなやり方では出来たのですが、
> グローバル変数を用いない方法はないでしょうか)。
という実装については提示されていないという理解でしたが、
もしかして、メンバ変数とグローバル変数を混同されていますか?
例えば、こんな風にしたかったとか?
explicit CScriptParser()
{
Sparser parser(name_buffer);
parseInfo = boost::spirit::classic::parse("a", parser, boost::spirit::classic::space_p);
}
> CScriptParser内のname_bufferに解析した値を持たせることはできないでしょうか。
Sparserを一度しか使わないなら、別段メンバ変数にする必要もありませんが、
もしかして、CScriptParserは結果だけを保持していれば良いのでしょうか?
(引き換えにパースする度にSparserを作成することになります)
Ban 2009/07/11(土) 23:05:04
はまだネストがおかしかったです…orz
エディタ等でネストして見てください。
パーサをコンストラクタで使い捨てる版をネストさせるとこんな感じ。
class CScriptParser
{
public:
// parse_info
boost::spirit::classic::parse_info<> parseInfo;
// 構文解析時のトークン保存用
std::string name_buffer;
struct Sparser : public boost::spirit::classic::grammar<Sparser>
{
template<typename S> struct definition {
typedef boost::spirit::classic::rule<S> rule_t;
rule_t A;
// 構文解析ルールの記述
definition(const Sparser& self)
{
A = boost::spirit::classic::alpha_p[boost::spirit::classic::assign_a(self.name_buffer_)];
}
const rule_t& start() const { return A; }
};
explicit Sparser(std::string& name_buffer) : name_buffer_(name_buffer) {}
std::string& name_buffer_;
};
explicit CScriptParser()
{
Sparser parser(name_buffer);
parseInfo = boost::spirit::classic::parse("a", parser, boost::spirit::classic::space_p);
}
};
とりあえず、文法定義時(definition(const Sparser& self))の引数
selfは「grammarの派生クラスである」というI/Fである以上、
本質的に結果に関する情報は、self(Sparser)を経由するのが正道だと思います。
例えば、上の例でname_buffer_が存在するのはそういう理由です。
そして、もしCScriptParserではなくname_buffer自体をSparserが持ってれば、
name_buffer_も不要になるわけですが、selfはconstなので結果を保持できません。
※言語的には"mutable"を使えばこういうこと(「Sparserはずし」)は可能ですが…用法にはご注意を。
# "Immutable Object"や"mutable"などが良く分からない場合はまずそちらから。
class CScriptParser : public boost::spirit::classic::grammar<CScriptParser>
{
public:
// parse_info
boost::spirit::classic::parse_info<> parseInfo;
// 構文解析時のトークン保存用
mutable std::string name_buffer;
template<typename S> struct definition {
typedef boost::spirit::classic::rule<S> rule_t;
rule_t A;
// 構文解析ルールの記述
definition(const CScriptParser& self)
{
A = boost::spirit::classic::alpha_p[boost::spirit::classic::assign_a(self.name_buffer)];
}
const rule_t& start() const { return A; }
};
explicit CScriptParser()
{
parseInfo = boost::spirit::classic::parse("a", *this, boost::spirit::classic::space_p);
}
};
# 連投失礼、これで最後にします。
>“1つの文字列を受け取り、
なので、
explicit CScriptParser()
{
parseInfo = boost::spirit::classic::parse("a", *this, boost::spirit::classic::space_p);
}
は
explicit CScriptParser(const char* str)
{
parseInfo = boost::spirit::classic::parse(str, *this, boost::spirit::classic::space_p);
}
とでもしてください。
すいません、見間違えてました……。
自分で整列させたネストが、一個分ずれてましたTT
道理で色々と混乱するわけ……。
本当に失礼しました。
mutableに関しては存じませんでした。
調べたところ、確かにこれを使えば色々な問題が一発で解決しますね。
ただ、使ってしまうと、本来の用法としては(const関数の意味としては)違った方向になってしまう気がしないでもないですね……。
でも、こちらの方がシンプルに記述できる分、また勉強になる意味でも、今回はmutableを使わせて頂きたいと思います。
ありがとうございました。
ツイート | ![]() |