如何打印在升压精神分析器符号表匹配的变量? [英] How to print the variables matched by the symbol table in Boost spirit parser?

查看:173
本文介绍了如何打印在升压精神分析器符号表匹配的变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用一个初学者提振精神

说我有以下code,它解析一个简单的算术EX pression与变量:

Say that I have the following code that parse a simple arithmetic expression with variables:

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/foreach.hpp>

#include <iostream>
#include <string>

namespace client {
    namespace ast
    {
        struct nil {};
        struct signed_;
        struct program;

        typedef boost::variant<
            nil
            , double
            , boost::recursive_wrapper<signed_>
            , boost::recursive_wrapper<program>
        >
        operand;

        struct signed_
        {
            char sign;
            operand operand_;
        };

        struct operation
        {
            char operator_;
            operand operand_;
        };

        struct program
        {
            operand first;
            std::list<operation> rest;
        };
    }
}

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::signed_,
    (char, sign)
    (client::ast::operand, operand_)
    )

    BOOST_FUSION_ADAPT_STRUCT(
    client::ast::operation,
    (char, operator_)
    (client::ast::operand, operand_)
    )

    BOOST_FUSION_ADAPT_STRUCT(
    client::ast::program,
    (client::ast::operand, first)
    (std::list<client::ast::operation>, rest)
    )

namespace client {
    namespace ast
    {
        struct eval
        {
            typedef double result_type;

            double operator()(nil) const { BOOST_ASSERT(0); return 0; }
            double operator()(double n) const { return n; }

            double operator()(operation const& x, double lhs) const
            {
                double rhs = boost::apply_visitor(*this, x.operand_);
                switch (x.operator_)
                {
                case '+': return lhs + rhs;
                case '-': return lhs - rhs;
                case '*': return lhs * rhs;
                case '/': return lhs / rhs;
                }
                BOOST_ASSERT(0);
                return 0;
            }

            double operator()(signed_ const& x) const
            {
                double rhs = boost::apply_visitor(*this, x.operand_);
                switch (x.sign)
                {
                case '-': return -rhs;
                case '+': return +rhs;
                }
                BOOST_ASSERT(0);
                return 0;
            }

            double operator()(program const& x) const
            {
                double state = boost::apply_visitor(*this, x.first);
                BOOST_FOREACH(operation const& oper, x.rest)
                {
                    state = (*this)(oper, state);
                }
                return state;
            }
        };
    }
}

namespace client
{
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    using boost::phoenix::function;

    template <typename Iterator>
    struct calculator : qi::grammar<Iterator, ast::program(), ascii::space_type>
    {
        calculator() : calculator::base_type(expression)
        {
            qi::char_type char_;
            qi::double_type doubleParser_;

            symboleTable.add("var1", 2);
            symboleTable.add("var2", 15);
            symboleTable.add("var4", 5);
            symboleTable.add("var", 5);
            symboleTable.add("x", 5);

            expression =
                term
                >> *((char_('+') > term)
                | (char_('-') > term)
                )
                ;

            term =
                factor
                >> *((char_('*') > factor)
                | (char_('/') > factor)
                )
                ;

            factor =
                doubleParser_
                | symbolTable
                | '(' > expression > ')'
                | (char_('-') > factor)
                | (char_('+') > factor)
                ;
        }
        qi::symbols<char, double> symbolTable;
        qi::rule<Iterator, ast::program(), ascii::space_type> expression;
        qi::rule<Iterator, ast::program(), ascii::space_type> term;
        qi::rule<Iterator, ast::operand(), ascii::space_type> factor;
    };
}

/////////////////////////////////////////////////////////////////////////////
//  Main program
/////////////////////////////////////////////////////////////////////////////
int
main()
{
    std::cout << "/////////////////////////////////////////////////////////\n\n";
    std::cout << "Expression parser...\n\n";
    std::cout << "/////////////////////////////////////////////////////////\n\n";
    std::cout << "Type an expression...or [q or Q] to quit\n\n";

    typedef std::string::const_iterator iterator_type;
    typedef client::calculator<iterator_type> calculator;
    typedef client::ast::program ast_program;
    typedef client::ast::eval ast_eval;

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

        calculator calc;        // Our grammar
        ast_program program;    // Our program (AST)
        ast_eval eval;          // Evaluates the program

        std::string::const_iterator iter = str.begin();
        std::string::const_iterator end = str.end();

        boost::spirit::ascii::space_type space;
        bool r = phrase_parse(iter, end, calc, space, program);

        if (r && iter == end)
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded\n";
            std::cout << "\nResult: " << eval(program) << std::endl;
            std::cout << "-------------------------\n";
        }
        else
        {
            std::string rest(iter, end);
            std::cout << "-------------------------\n";
            std::cout << "Parsing failed\n";
            std::cout << "-------------------------\n";
        }
    }

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

我要打印的变量(不它们的值)时,它们是由符号表(在语法中声明)匹配。

I want to print the variables (not their values) when they are matched by the symbol table (declared in the grammar).

因此​​,例如当输入为

So for example when the input is

var* 2 - 3 +x*var2 - 2

输出应该是:

var
x
var2

任何帮助吗?

推荐答案

用于不存储原始变量的引用AST

The AST used doesn't store the original variable referenced.

因此​​, 解析的信息不再可用(AST的只包含价值节点,而不是原来的参考)后。

Hence after parsing the information is no longer available (the AST just contains value nodes instead of the original reference).

有两种方式这一点:


  • 让您解决在仅评估时间变量(保持变量引用名称)丰富AST

  • enrich the AST so you resolve variables at evaluation time only (keeping the variable reference names)

更新我已经加入另一个答案真正实现这一点,更复杂,办法

UPDATE I have added another answer that actually implements this, more elaborate, approach

已经解析器解析过程中收集的变量引用出带外。

  • have the parser collect variable references "out-of-band" during parsing.

    后者需要极大地努力,小(如果你知道圣灵+凤凰的技巧)。因此,让我们表明:

    The latter requires vastly smaller effort (if you know the tricks of Spirit + Phoenix). So let's show that:

            factor =
                doubleParser_
                | variable
                | '(' > expression > ')'
                | (char_('-') > factor)
                | (char_('+') > factor)
                ;
    

    下面我更换了 symbolTable 按新的规则:变量

    Here I replaced the symbolTable by a new rule: variable:

        qi::rule<Iterator, double()> variable; // NOTE: also made it a lexeme (no skipper)
    

    这条规则还只是暴露值作为一个副作用,我们将它收集的参考为一组变量名称:

    That rule still exposes just the value but as a side effect we will have it collect the reference into a set of variable names:

            variable %=  
                   &qi::as_string[qi::raw[symbolTable]] 
                         [ px::insert(px::ref(collect_references), qi::_1) ] 
                >> symbolTable
                ;
    

    正如你所看到的,它是一个快速和肮脏的方式充分利用了很多精神技巧(操作符%= 自动规则分配,补气生:: 和齐:: as_string 指令,凤凰::插入和通过积极前瞻断言第二解析(运营商的放大器; )。

    As you can see, it is a quick-and-dirty approach leveraging a lot of Spirit tricks (operator%= auto-rule assignment, qi::raw and qi::as_string directives, phoenix::insert and the second parse by using the positive look-ahead assertion (operator&).

    现在,我们只需要在 collect_references 容器语法通过,我们可以打印成功后,解析引用:

    Now, we just need to pass in a collect_references container to the grammar, and we can print the references after successful parsing:

        std::set<std::string> collected_references;
        calculator calc(collected_references); // Our grammar
    
        if (r && iter == end)
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded\n";
            std::cout << "References: ";
            std::copy(collected_references.begin(), collected_references.end(),
                    std::ostream_iterator<std::string>(std::cout, " "));
    
            std::cout << "\nResult: " << eval(program) << std::endl;
            std::cout << "-------------------------\n";
        }
    

    它打印:

    Type an expression...or [q or Q] to quit
    
    var* 2 - 3 +x*var2 - 2
    -------------------------
    Parsing succeeded
    References: var var2 x 
    Result: 80
    -------------------------
    Bye... :-) 
    

    DEMO code

    <大骨节病> 住在Coliru

    #include <boost/config/warning_disable.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/variant/recursive_variant.hpp>
    #include <boost/variant/apply_visitor.hpp>
    #include <boost/fusion/include/adapt_struct.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <boost/foreach.hpp>
    
    #include <iostream>
    #include <string>
    #include <set>
    
    namespace client {
        namespace ast
        {
            struct nil {};
            struct signed_;
            struct program;
    
            typedef boost::variant<
                nil
                , double
                , boost::recursive_wrapper<signed_>
                , boost::recursive_wrapper<program>
            >
            operand;
    
            struct signed_
            {
                char sign;
                operand operand_;
            };
    
            struct operation
            {
                char operator_;
                operand operand_;
            };
    
            struct program
            {
                operand first;
                std::list<operation> rest;
            };
        }
    }
    
    BOOST_FUSION_ADAPT_STRUCT(
        client::ast::signed_,
        (char, sign)
        (client::ast::operand, operand_)
        )
    
        BOOST_FUSION_ADAPT_STRUCT(
        client::ast::operation,
        (char, operator_)
        (client::ast::operand, operand_)
        )
    
        BOOST_FUSION_ADAPT_STRUCT(
        client::ast::program,
        (client::ast::operand, first)
        (std::list<client::ast::operation>, rest)
        )
    
    namespace client {
        namespace ast
        {
            struct eval
            {
                typedef double result_type;
    
                double operator()(nil) const { BOOST_ASSERT(0); return 0; }
                double operator()(double n) const { return n; }
    
                double operator()(operation const& x, double lhs) const
                {
                    double rhs = boost::apply_visitor(*this, x.operand_);
                    switch (x.operator_)
                    {
                    case '+': return lhs + rhs;
                    case '-': return lhs - rhs;
                    case '*': return lhs * rhs;
                    case '/': return lhs / rhs;
                    }
                    BOOST_ASSERT(0);
                    return 0;
                }
    
                double operator()(signed_ const& x) const
                {
                    double rhs = boost::apply_visitor(*this, x.operand_);
                    switch (x.sign)
                    {
                    case '-': return -rhs;
                    case '+': return +rhs;
                    }
                    BOOST_ASSERT(0);
                    return 0;
                }
    
                double operator()(program const& x) const
                {
                    double state = boost::apply_visitor(*this, x.first);
                    BOOST_FOREACH(operation const& oper, x.rest)
                    {
                        state = (*this)(oper, state);
                    }
                    return state;
                }
            };
        }
    }
    
    namespace client
    {
        namespace qi = boost::spirit::qi;
        namespace ascii = boost::spirit::ascii;
    
        template <typename Iterator>
        struct calculator : qi::grammar<Iterator, ast::program(), ascii::space_type>
        {
            calculator(std::set<std::string>& collect_references) : calculator::base_type(expression)
            {
                qi::char_type char_;
                qi::double_type doubleParser_;
    
                symbolTable.add("var1", 2);
                symbolTable.add("var2", 15);
                symbolTable.add("var4", 5);
                symbolTable.add("var",  5);
                symbolTable.add("x",    5);
    
                namespace px = boost::phoenix;
    
                expression =
                    term
                    >> *((char_('+') > term)
                     |  (char_('-') > term)
                    )
                    ;
    
                term =
                    factor
                    >> *((char_('*') > factor)
                    | (char_('/') > factor)
                    )
                    ;
    
                variable %=  
                       &qi::as_string[qi::raw[symbolTable]] 
                             [ px::insert(px::ref(collect_references), qi::_1) ] 
                    >> symbolTable
                    ;
    
                factor =
                    doubleParser_
                    | variable
                    | ('(' > expression > ')')
                    | (char_('-') > factor)
                    | (char_('+') > factor)
                    ;
            }
          private:
            qi::symbols<char, double> symbolTable;
            qi::rule<Iterator, double()> variable; // NOTE: also made it a lexeme (no skipper)
            qi::rule<Iterator, ast::program(), ascii::space_type> expression;
            qi::rule<Iterator, ast::program(), ascii::space_type> term;
            qi::rule<Iterator, ast::operand(), ascii::space_type> factor;
        };
    }
    
    /////////////////////////////////////////////////////////////////////////////
    //  Main program
    /////////////////////////////////////////////////////////////////////////////
    int
    main()
    {
        std::cout << "/////////////////////////////////////////////////////////\n\n";
        std::cout << "Expression parser...\n\n";
        std::cout << "/////////////////////////////////////////////////////////\n\n";
        std::cout << "Type an expression...or [q or Q] to quit\n\n";
    
        typedef std::string::const_iterator iterator_type;
        typedef client::calculator<iterator_type> calculator;
        typedef client::ast::program ast_program;
        typedef client::ast::eval ast_eval;
    
        std::string str;
        while (std::getline(std::cin, str))
        {
            if (str.empty() || str[0] == 'q' || str[0] == 'Q')
                break;
    
            std::set<std::string> collected_references;
            calculator calc(collected_references); // Our grammar
            ast_program program;                   // Our program (AST)
            ast_eval eval;                         // Evaluates the program
    
            std::string::const_iterator iter = str.begin();
            std::string::const_iterator end = str.end();
    
            boost::spirit::ascii::space_type space;
            bool r = phrase_parse(iter, end, calc, space, program);
    
            if (r && iter == end)
            {
                std::cout << "-------------------------\n";
                std::cout << "Parsing succeeded\n";
                std::cout << "References: ";
                std::copy(collected_references.begin(), collected_references.end(),
                        std::ostream_iterator<std::string>(std::cout, " "));
    
                std::cout << "\nResult: " << eval(program) << std::endl;
                std::cout << "-------------------------\n";
            }
            else
            {
                std::string rest(iter, end);
                std::cout << "-------------------------\n";
                std::cout << "Parsing failed\n";
                std::cout << "-------------------------\n";
            }
        }
    
        std::cout << "Bye... :-) \n\n";
        return 0;
    }
    

    这篇关于如何打印在升压精神分析器符号表匹配的变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆