boost phoenix 绑定具有多个参数和 return 值的语义操作

boost phoenix bind a semantic action with multiple parameter and a return value

我对 C++ 和 Boost 精神都是新手。

我现在被困在这个问题上一天了。 我想解析由点分隔的两个字符串。 基本上,我需要将以下字符串解析为整数。

例如:[字段] --> 整数 // 有效

eg2: [instance.field] --> 整数 // 不工作

对于第二个,我需要将两个字符串作为参数并计算它们和 return 相关的整数值。

我肯定漏掉了一个基本的东西,但我想不通。

请让我知道我的代码中的错误或更好的方法。 需要调用方法并获取值。我无法改变这一点。

这是密码和平。

#define BOOST_SPIRIT_USE_PHOENIX_V3

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_utree.hpp>

#include <iostream>
#include <string>


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


int fieldVal(std::vector<char> fieldName) { 
    std::string fieldValue(fieldName.begin(), fieldName.end());
    std::cout << "Field Name Recieved.::: " << fieldValue << std::endl;
    int i = 50; //just for test 
    return i;
}

int instanceFieldVal(std::string instance, std::string fieldName) {

    std::cout << "Recieved ::: " << instance <<" : "<< fieldName << std::endl;
    int i = 60; //just for test
    return i;
}

namespace client
{
    template <typename Iterator>
    struct calculator : qi::grammar<Iterator, ascii::space_type, int()>
    {
        calculator() : calculator::base_type(instanceFieldValue)
      /*change base type to "field" and comment everything relevant to 
        "instanceFieldValue", then it's working */

        {
            using qi::int_;
            using qi::_val;
            using qi::_1;
            using qi::char_;
            using qi::lexeme;

            field = lexeme[
                '['
                    >> +(~char_(".]["))
                    >> ']'
            ][qi::_val = phx::bind(&fieldVal, qi::_1)]; // this is working


            instanceField = '['
                    >> +(~char_(".]["))
                    >> '.'
                    >> +(~char_(".]["))
                    >> ']';

            instanceFieldValue 
                = instanceField[qi::_val = phx::bind(&instanceFieldVal, qi::_1)]; 
            // how ^this line should be changed??

        }

        qi::rule<Iterator, ascii::space_type, int()> field, instanceFieldValue;

        qi::rule<Iterator, ascii::space_type, std::string(), std::string()>instanceField;
    };
}


int main()
{

    std::cout << "Type an expression...or [q or Q] to quit\n\n";

    using boost::spirit::ascii::space;
    using boost::spirit::utree;
    typedef std::string::const_iterator iterator_type;
    typedef client::calculator<iterator_type> calculator;

    calculator calc; // Our grammar

    std::string str;
    while (std::getline(std::cin, str))
    {
        if (str.empty() || str[0] == 'q' || str[0] == 'Q')
            break;

        std::string::const_iterator iter = str.begin();
        std::string::const_iterator end = str.end();
        int val;
        bool r = phrase_parse(iter, end, calc, space, val);

        if (r && iter == end)
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded: " << val << "\n";
            std::cout << "-------------------------\n";
        }
        else
        {
            std::string rest(iter, end);
            std::cout << "-------------------------\n";
            std::cout << "Parsing failed\n";
            std::cout << "stopped at: \": " << rest << "\"\n";
            std::cout << "-------------------------\n";
        }
    }

    std::cout << "Bye... :-) \n\n";
    return 0;
}

好吧,你它是一个有多个参数的函数,但你只传递了一个参数:_1.

这怎么行?它不能。它也完全是您想要传递的内容,因为第二个参数可能来自 instanceField 表达式,但第一个参数是......神奇的上下文。

像往常一样,我会尽量减少状态和语义操作。事实上,我建议关注点分离:

  1. 先解析为std:vector<std::string>
  2. 成功解析后,对其进行评估

这会导致像这样的语法

template <typename Iterator>
struct path : qi::grammar<Iterator, std::deque<std::string>()> {
    path() : path::base_type(start) {
        using namespace qi;

        name          = +(graph - char_(".][")); // not matching spaces please
        qualifiedName = name % '.';

        start = skip(ascii::space) ['[' >> qualifiedName >> ']'];

        BOOST_SPIRIT_DEBUG_NODES((start)(qualifiedName)(name))
    }

  private:
    qi::rule<Iterator, std::deque<std::string>(), qi::ascii::space_type> qualifiedName;
    qi::rule<Iterator, std::string()> name;
    qi::rule<Iterator, std::deque<std::string>()> start;
};

当然可以如果你坚持的话,当然可以使用语义动作将它组合起来:

template <typename Iterator>
struct property : qi::grammar<Iterator, int()> {
    property() : property::base_type(start) {
        using namespace qi;

        name          = +(graph - char_(".][")); // not matching spaces please
        qualifiedName = name % '.';

        start = skip(ascii::space) ['[' >> qualifiedName >> ']']
                        [_val = phx::bind(lookup, phx::cref(sample), _1) ]
                ;

        BOOST_SPIRIT_DEBUG_NODES((start)(qualifiedName)(name))
    }

  private:
    qi::rule<Iterator, std::deque<std::string>(), qi::ascii::space_type> qualifiedName;
    qi::rule<Iterator, std::string()> name;
    qi::rule<Iterator, int()> start;
};

现在,只需实现一个功能

int lookup(Node const& context, std::deque<std::string> path);

举个例子,让我们想象一个示例上下文:

using Leaf    = int;
using Node    = boost::make_recursive_variant< Leaf, std::map<std::string, boost::recursive_variant_> >::type;
using SubTree = std::map<std::string, Node>;

static Node sample = SubTree {
    { "simple", 100 },
    { "compound", SubTree { 
          { "first", 200 },
          { "second", 300 },
      }, },
    { "deep", SubTree {  
        { "nested", SubTree { 
            { "compound", SubTree { 
                  { "buried", 400 },
                  { "secrets", 500 },
    }, }, }, }, }, }
};

现在,lookup 的实现可以像这样简单:

int lookup(Node const& context, std::deque<std::string> path) {
    if (path.empty())
        return boost::get<Leaf>(context);

    auto& sub = boost::get<SubTree>(context);

    auto element = path.front();
    path.pop_front();

    try {
        return lookup(sub.at(element), std::move(path));
    } catch(std::out_of_range const& e) {
        throw std::runtime_error("'" + element + "' not found");
    }
}

完整演示

Live On Coliru

//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iostream>
#include <map>

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

using Leaf    = int;
using Node    = boost::make_recursive_variant< Leaf, std::map<std::string, boost::recursive_variant_> >::type;
using SubTree = std::map<std::string, Node>;

static Node sample = SubTree {
    { "simple", 100 },
    { "compound", SubTree { 
              { "first", 200 },
              { "second", 300 },
          }, },
    { "deep", SubTree {  
            { "nested", SubTree { 
                    { "compound", SubTree { 
                              { "buried", 400 },
                              { "secrets", 500 },
                          },
                    },
                },
            },
        },
    }
};

int lookup(Node const& context, std::deque<std::string> path) {
    if (path.empty())
        return boost::get<Leaf>(context);

    auto& sub = boost::get<SubTree>(context);

    auto element = path.front();
    path.pop_front();

    try {
        return lookup(sub.at(element), std::move(path));
    } catch(std::out_of_range const& e) {
        throw std::runtime_error("'" + element + "' not found");
    }
}

namespace parser {

    template <typename Iterator>
    struct property : qi::grammar<Iterator, int()> {
        property() : property::base_type(start) {
            using namespace qi;

            name          = +(graph - char_(".][")); // not matching spaces please
            qualifiedName = name % '.';

            start = skip(ascii::space) ['[' >> qualifiedName >> ']']
                            [_val = phx::bind(lookup, phx::cref(sample), _1) ]
                    ;

            BOOST_SPIRIT_DEBUG_NODES((start)(qualifiedName)(name))
        }

      private:
        qi::rule<Iterator, std::deque<std::string>(), qi::ascii::space_type> qualifiedName;
        qi::rule<Iterator, std::string()> name;
        qi::rule<Iterator, int()> start;
    };
}

int main() {
    using It =  std::string::const_iterator;
    parser::property<It> calc;

    for (std::string const str : {
                "[simple]",
                "[compound.first]",
                "[compound.second]",
                "[deep.nested.compound.buried]",
                "[deep.nested.compound.secrets]",
                // whitespace is ok
                "  [ compound.\tfirst ]",
                // failing:
                "[]",
                "[missing]",
                "[deep.missing.compound.buried]",
                // whitespace not ok inside names
                "  [ compound.\tfi rst ]",
            })
    try {
        std::cout << " ===== Input: '" << str << "'\n";
        It iter = str.begin(), end = str.end();

        int val;
        bool r = parse(iter, end, calc, val);

        if (r) {
            std::cout << "Parsing succeeded: " << val << "\n";
        } else {
            std::cout << "Parsing failed\n";
        }

        if (iter != end) {
            std::cout << " - Remaining unparsed input: '" << std::string(iter, end) << "'\n";
        }
    } catch(std::exception const& e) {
        std::cout << "Exception: " << e.what() << "\n";
    }
}

打印:

 ===== Input: '[simple]'
Parsing succeeded: 100
 ===== Input: '[compound.first]'
Parsing succeeded: 200
 ===== Input: '[compound.second]'
Parsing succeeded: 300
 ===== Input: '[deep.nested.compound.buried]'
Parsing succeeded: 400
 ===== Input: '[deep.nested.compound.secrets]'
Parsing succeeded: 500
 ===== Input: '  [ compound.    first ]'
Parsing succeeded: 200
 ===== Input: '[]'
Parsing failed
 - Remaining unparsed input: '[]'
 ===== Input: '[missing]'
Exception: 'missing' not found
 ===== Input: '[deep.missing.compound.buried]'
Exception: 'missing' not found
 ===== Input: '  [ compound.    fi rst ]'
Parsing failed
 - Remaining unparsed input: '  [ compound. fi rst ]'