boost::spiritを使っての構文解析器を、1つのクラスにまとめたい

解決


PenName  2009-07-10 17:30:29  No: 70532

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に解析した値を持たせることはできないでしょうか。
(グローバル変数にて実装するようなやり方では出来たのですが、グローバル変数を用いない方法はないでしょうか)。

よろしくお願いします。


Ban  2009-07-11 23:32:33  No: 70533

オリジナルを残してあまり弄らないように書いたつもりですが、
例えばこんな方法(参照で渡しておく)とか。

(なお、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);
  }
};


PenName  2009-07-12 04:31:29  No: 70534

ご返信ありがとうございます。
こういう描き方もあるんですね。
勉強になります……。

しかし、Sparserが
Sparser parser;
という形でグローバル変数があるのが気になります。。。
どうにかならないものでしょうか。


Ban  2009-07-12 08:05:04  No: 70535

> という形でグローバル変数があるのが気になります。。。

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);
    }
};


Ban  2009-07-12 08:18:59  No: 70536

何を望んでるのか、よく分からなくなりましたが、
「メンバ変数」と「グローバル変数」の違いは理解されていますか。

参照渡しや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-12 08:20:49  No: 70537

Ban 2009/07/11(土) 23:05:04
はまだネストがおかしかったです…orz
エディタ等でネストして見てください。


Ban  2009-07-12 08:23:47  No: 70538

パーサをコンストラクタで使い捨てる版をネストさせるとこんな感じ。

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);
    }
};


Ban  2009-07-12 09:04:45  No: 70539

とりあえず、文法定義時(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);
    }
};


Ban  2009-07-12 09:07:44  No: 70540

# 連投失礼、これで最後にします。

>“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);
    }
とでもしてください。


PenName  2009-07-12 17:49:20  No: 70541

すいません、見間違えてました……。
自分で整列させたネストが、一個分ずれてましたTT
道理で色々と混乱するわけ……。
本当に失礼しました。

mutableに関しては存じませんでした。
調べたところ、確かにこれを使えば色々な問題が一発で解決しますね。
ただ、使ってしまうと、本来の用法としては(const関数の意味としては)違った方向になってしまう気がしないでもないですね……。
でも、こちらの方がシンプルに記述できる分、また勉強になる意味でも、今回はmutableを使わせて頂きたいと思います。

ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加