How to print the variables matched by the symbol table in Boost spirit parser?

前端 未结 2 395
感情败类
感情败类 2021-01-01 05:09

I am a beginner in using boost spirit

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

#incl         


        
2条回答
  •  太阳男子
    2021-01-01 05:54

    The first, more involved approach described here modifies the AST to represent the variable references.

    This has multiple advantages:

    • the richer information allows you to do e.g. collection of variable references after the parse
    • the delayed evaluation of variables allows reuse of the same AST with a different set of values for the variables! This is more like how actual interpreters work.

    Let's start:

    1. Modifying the AST:

      We want to be able to have a variable name in addition to the existing types of expressions:

      typedef boost::variant<
          nil
          , std::string // THIS LINE ADDED
          , double
          , boost::recursive_wrapper
          , boost::recursive_wrapper
      >
      operand;
      
    2. Modifying the rule:

      Wwe want to keep the name of the variable instead of immediately replacing it by a fixed value:

          factor =
                qi::double_
              | qi::as_string[qi::raw[symbolTable]] // THIS LINE CHANGED
              | '(' > expression > ')'
              | (char_('-') > factor)
              | (char_('+') > factor)
              ;
      
    3. Modifying the evalation visitor

      Now we need to "replace" the variables by their value during evaluation.

      Let's add a simple overload to the visitor:

      double operator()(std::string const& var) const { return symbols.at(var);    } 
      

      We have given the visitor a reference to a map symbols for lookup:

      std::map& symbols;
      eval(std::map& symbols) : symbols(symbols) {}
      
    4. Calling it from main:

      So we need to have a map of variables handy:

      std::map symbols { {"var1", 2}, {"var2", 15}, {"var4", 5}, {"var", 5}, {"x", 5} };
      

      And pass a reference to the visitor:

      ast_eval eval(symbols) ; // Evaluates the program
      

    At this point, the program operates exactly as the original program, but with an enriched AST:

    Live On Coliru

    Printing

    -------------------------
    Parsing succeeded
    
    Result: 80
    -------------------------
    

    Enumerating variables referenced!

    The point of the story becomes simple now: we just define another visitor like eval to extract references:

    namespace client { namespace ast {
        struct extract_refs : boost::static_visitor
        {
            std::set& _references;
    
            extract_refs(std::set& refs) : _references(refs) {}
    
            void operator()(std::string const& var) const { _references.insert(var);                 } 
            void operator()(operation const& x) const     { boost::apply_visitor(*this, x.operand_); } 
            void operator()(signed_ const& x) const       { boost::apply_visitor(*this, x.operand_); } 
            void operator()(program const& x) const       {
                boost::apply_visitor(*this, x.first);
                BOOST_FOREACH(operation const& oper, x.rest) (*this)(oper);
            }
    
            // ignore anything else
            template  void operator()(Other const&) const {}
        };
    } }
    

    This can simply be applied to the AST:

    std::set references;
    client::ast::extract_refs extract(references);
    
    extract(program);
    
    std::cout << "References: ";
    std::copy(references.begin(), references.end(), std::ostream_iterator(std::cout, " "));
    

    And the output once again becomes

    -------------------------
    Parsing succeeded
    References: var var2 x 
    Result: 80
    -------------------------
    

    Quod Erat Demonstrandum.

    Live On Coliru

提交回复
热议问题