【问题标题】:boost phoenix bind a semantic action with multiple parameter and a return valueboost phoenix 用多个参数和一个返回值绑定一个语义动作
【发布时间】:2017-05-26 10:44:22
【问题描述】:

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

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

eg: [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;
}

【问题讨论】:

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


    【解决方案1】:

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

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

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

    1. 首先解析到std:vector&lt;std::string&gt;
    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 ]'
    

    【讨论】:

    • 回复丢失的评论 (?) - 我将语法绑定到示例,因为那是您正在搜索的行(“您当然可以使用语义操作来组合它,如果您坚持”)。显然,我的建议是"In fact, I'd suggest separation of concerns: 1. parse to std:vector&lt;std::string&gt; first 2. on successful parse, evaluate it_
    • 谢谢,我通过研究你的代码得到了我需要的答案。对于缺少的评论,我在一分钟后意识到自己评论的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-19
    • 2012-04-02
    • 2021-04-10
    • 2014-12-12
    • 2023-03-22
    • 2013-02-11
    • 1970-01-01
    相关资源
    最近更新 更多