boost phoenix使用多个参数和返回值绑定语义动作

时间:2017-05-26 10:44:22

标签: boost boost-spirit boost-bind boost-phoenix

我是C ++和Boost精神的新手。

我现在已经坚持了一天。 我想解析两个用点分隔的字符串。 基本上,我需要将以下字符串解析为整数。

例如:[field] - >整数//工作

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

对于第二个,我需要将两个字符串作为参数并对其进行评估并返回相关的整数值。

我一定错过了一个基本的东西,但我无法理解。

请告诉我代码中的错误或更好的方法。 调用方法并获取值是必需的。我无法改变这一点。

这是代码和平。

#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 个答案:

答案 0 :(得分:1)

嗯,你这是一个有多个参数的函数,但是你只传递了一个arg:_1

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

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

  1. 首先解析为std:vector<std::string>
  2. 成功解析,评估
  3. 这会产生类似

    的语法
    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 ]'