5

代数式を解析して評価する代わりに、パーサーが代数ステートメントが真であるかどうかを判断するように、電卓の例を拡張しようとしています。これは、1 + 5 * 5 - 10 = 19 - 3(望ましいパーサー結果はtrue)や3 - 1 = 9(望ましいパーサー結果はfalse)のようなステートメントを意味します。

私は初めてのことを認めなければなりません、boost::spiritそしてそれは今のところすべての種類の圧倒的です。ただし、電卓の例は、少なくともある程度前進するのに十分理解していると思います。

提供された例を開始点として使用すると、文法は次のようになります。

calculator() : calculator::base_type(expression)
{
    using qi::uint_;
    using qi::_val;
    using qi::_1;

    expression =
        term                    [_val = _1]
            >> *( ('+' >> term  [_val = _val + _1])
                | ('-' >> term  [_val = _val - _1])
                );

        term =
            factor                [_val = _1]
            >> *( ('*' >> factor  [_val = _val * _1])
                | ('/' >> factor  [_val = _val / _1])
                );

        factor =
            uint_                 [_val = _1]
            |   '(' >> expression [_val = _1] >> ')'
            |   ('-' >> factor    [_val = -_1])
            |   ('+' >> factor    [_val = _1]);
}

簡潔にするために、デバッグマクロを削除しました。

問題の範囲を制限するために、ステートメントごとに1つの等号のみを許可することにしました。閉じた括弧のペアの中に等号を表示することは(少なくとも通常の意味では)無意味なので、括弧も許可しないことにしました。これfactorにより、オプションのの削除が許可されるため、-parserが単純化されます'(' >> expression [_val = _1] >> ')'

この時点で私は少し立ち往生しています。まず、単一の等号を受け入れるパーサーが必要です。次に、最終的に比較を実行する前に、ステートメントの左側(LHS)と右側(RHS)を個別に評価するためのセマンティックアクションが必要です(つまり、これを実行する必要があると思います)。

最も簡単なアプローチは、等号に一致する3番目のパーサーで区切られた2つの別々のパーサー(1つはLHSと1つはRHS)を作成することであるかどうか疑問に思います。2つのパーサーLHSとRHSは、これらを最終的に比較するために入力を2つの異なるカテゴリーに明確に分離する必要があるセマンティックアクションを除いて、同一である必要があります。

2つの別々のパーサーLHSとRHSを作成する前に、評価された式がローカル変数に格納されるように元のパーサーを変更する方法を学びたいと思いました。(それがどこへの実行可能な道であるかさえわかりませんが、それは正しい方向への一歩のようです。)

これは私が試したものです:

int result;

expression =
    term                            [result = _1]
    >> *(   ('+' >> term            [result = result + _1])
        |   ('-' >> term            [result = result - _1])
        );

しかし、これは私のコンパイラ(Apple LLVMコンパイラ4.2、Xcode 4.6)を狂わせ、私にそれを怒鳴りつけます

互換性のない型'const_1_type'から'int'に割り当てます(別名'constactor<引数<0>>')

後から考えると、これはもちろん理にかなっています。なぜなら、そもそも_valバインドさintれていなかったからです(結局のところ、パーサーはAFAIUであり、一般的であると考えられています)。つまり、評価された解析式を一時的に格納するために使用する型を定義する方法を理解する必要があります。

問題は、誰かが私に正しい方向へのナッジを与えることができるかということです。LHSとRHSの分割は進むべき道のように見えますか?

どんな提案でも大歓迎です!

4

1 に答える 1

8

あなたが私に尋ねれば、うまくいく可能性のある最も簡単なことはhttp://liveworkspace.org/code/1fvc8x$0でしょう

equation = (expression >> "=" >> expression) [ _val = _1 == _2 ];

これにより2つの式が解析され、返される属性は、bool両方の式が同じ値に評価されたかどうかを示すaです。

デモンストレーションプログラム

int main()
{
    doParse("1 + 2 * 3 = 7");
    doParse("1 + 2 * 3 = 8");
}

プリント

parse success
result: true
parse success
result: false

サンプルプログラム

#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi    = boost::spirit::qi;
namespace phx   = boost::phoenix;

typedef unsigned attr_t;

template <typename It, typename Skipper = qi::space_type>
    struct calculator : qi::grammar<It, bool(), Skipper>
{
    calculator() : calculator::base_type(equation)
    {
        using qi::uint_;
        using qi::_val;
        using qi::_1;
        using qi::_2;

        equation = (expression >> "=" >> expression) [ _val = _1 == _2 ];

        expression =
            term                    [_val = _1]
                >> *( ('+' >> term  [_val = _val + _1])
                    | ('-' >> term  [_val = _val - _1])
                    );

            term =
                factor                [_val = _1]
                >> *( ('*' >> factor  [_val = _val * _1])
                    | ('/' >> factor  [_val = _val / _1])
                    );

            factor =
                uint_                 [_val = _1]
                |   '(' >> expression [_val = _1] >> ')'
                |   ('-' >> factor    [_val = -_1])
                |   ('+' >> factor    [_val = _1]);
    }

  private:
    qi::rule<It, unsigned(), Skipper> expression, term, factor;
    qi::rule<It, bool(), Skipper> equation;
};

bool doParse(const std::string& input)
{
    typedef std::string::const_iterator It;
    auto f(begin(input)), l(end(input));

    calculator<It, qi::space_type> p;
    bool result;

    try
    {
        bool ok = qi::phrase_parse(f,l,p,qi::space,result);
        if (ok)   
        {
            std::cout << "parse success\n";
            std::cout << "result: " << std::boolalpha << result << "\n";
        }
        else      std::cerr << "parse failed: '" << std::string(f,l) << "'\n";

        if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
        return ok;
    } catch(const qi::expectation_failure<It>& e)
    {
        std::string frag(e.first, e.last);
        std::cerr << e.what() << "'" << frag << "'\n";
    }

    return false;
}

int main()
{
    doParse("1 + 2 * 3 = 7");
    doParse("1 + 2 * 3 = 8");
}
于 2013-02-27T22:42:02.633 に答える