2011-12-10 19 views
8

Dopo aver letto il tutorials su boost::spirit, mi è piaciuto molto a causa della sintassi del combinatore parser. Fare un parser è così facile.Recupero AST da boost :: parser spirito

Sfortunatamente, le esercitazioni non erano così precise sull'ottenere dal parser una struttura dati complessa. Sto cercando di arrivare allo Kaleidoscope AST.

In ogni caso, qui essere il mio codice AST:

#ifndef __AST_HPP__ 
#define __AST_HPP__ 

#include <boost/fusion/include/adapt_struct.hpp> 
#include <boost/variant/recursive_variant.hpp> 
#include <boost/lexical_cast.hpp> 
#include <boost/variant/apply_visitor.hpp> 
#include <string> 
#include <vector> 

namespace ast { 

struct add; 
struct sub; 
struct mul; 
struct div; 
struct func_call; 
template<typename OpTag> struct binary_op; 

typedef boost::variant<double, std::string, boost::recursive_wrapper<binary_op< 
     add>>, boost::recursive_wrapper<binary_op<sub>>, 
     boost::recursive_wrapper<binary_op<mul>>, boost::recursive_wrapper< 
       binary_op<div>>, boost::recursive_wrapper<func_call>> 
     expression; 

template<typename OpTag> 
struct binary_op { 
    expression left; 
    expression right; 

    binary_op(const expression & lhs, const expression & rhs) : 
     left(lhs), right(rhs) { 
    } 
}; 

struct func_call { 
    std::string callee; 
    std::vector<expression> args; 

    func_call(const std::string func, const std::vector<expression> &args) : 
     callee(func), args(args) { 
    } 
}; 

struct prototype { 
    std::string name; 
    std::vector<std::string> args; 

    prototype(const std::string &name, const std::vector<std::string> &args) : 
     name(name), args(args) { 
    } 
}; 

struct function { 
    prototype proto; 
    expression body; 

    function(const prototype &proto, const expression &body) : 
     body(body), proto(proto) { 
    } 
}; 

} 
    #endif 

ho omesso le BOOST_FUSION_ADAPT_STRUCT parti, ma ci sono.

E questa mia espressione parser:

#ifndef __PARSER_HPP__ 
#define __PARSER_HPP__ 

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

#include "ast.hpp" 

namespace parser { 

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

template<typename Iterator> 
struct expression: qi::grammar<Iterator, ast::expression(), ascii::space_type> { 
    expression() : 
     expression::base_type(expr) { 
     using qi::lit; 
     using qi::lexeme; 
     using ascii::char_; 
     using ascii::string; 
     using ascii::alnum; 
     using ascii::alpha; 
     using qi::double_; 
     using namespace qi::labels; 

     using phoenix::at_c; 
     using phoenix::push_back; 

     number %= lexeme[double_]; 
     varname %= lexeme[alpha >> *(alnum | '_')]; 

     binop 
       = (expr >> '+' >> expr)[_val = ast::binary_op<ast::add>(_1, _3)] 
         | (expr >> '-' >> expr)[_val 
           = ast::binary_op<ast::sub>(_1, _3)] 
         | (expr >> '*' >> expr)[_val 
           = ast::binary_op<ast::mul>(_1, _3)] 
         | (expr >> '/' >> expr)[_val 
           = ast::binary_op<ast::div>(_1, _3)]; 

     expr %= number | varname | binop; 
    } 

    qi::rule<Iterator, ast::expression(), ascii::space_type> expr; 
    qi::rule<Iterator, ast::expression(), ascii::space_type> binop; 
    qi::rule<Iterator, std::string, ascii::space_type> varname; 
    qi::rule<Iterator, double, ascii::space_type> number; 
}; 

} 

#endif 

Il problema che ho è che sembra avere un problema con il conseguente ast::expression. La compilazione genera più di 200 righe di errori di modello complessi. Sospetto che sia qualcosa con il modo in cui ho cercato di ottenere le informazioni dalla regola binop, ma non ne sono sicuro.

Qualcuno può aiutare?

risposta

7

Si sta tentando di chiamare il costruttore ast::binary_op utilizzando i segnaposti di Boost Phoenix. Non si mescolano bene. È necessario utilizzare un lazy call per il costruttore ast::binary_op. Questo è fornito a Phoenix utilizzando construct:

binop = (expr >> '+' >> expr) [_val = construct< ast::binary_op<ast::add> >(_1, _2)] 
     | (expr >> '-' >> expr) [_val = construct< ast::binary_op<ast::sub> >(_1, _2)] 
     | (expr >> '*' >> expr) [_val = construct< ast::binary_op<ast::mul> >(_1, _2)] 
     | (expr >> '/' >> expr) [_val = construct< ast::binary_op<ast::div> >(_1, _2)] ; 

Inoltre, penso che è necessario solo i _1 e _2 segnaposto, come il '+', '-', ... vengono convertiti in qi::lit (letterale) avendo così nessun attributo.

Ho anche notato un paio di parentesi mancante nel varname e number regole:

qi::rule<Iterator, std::string(), ascii::space_type> varname; 
//       ^^   
qi::rule<Iterator, double(), ascii::space_type> number; 
//      ^^ 

Boost Spirito Qi è molto potente, ma anche molto difficile da eseguire il debug. Quando ho iniziato a usarlo, ho trovato questi Boost Spirit Applications molto utili.

Spero che questo sia utile, poiché non sono un esperto di Boost Spirit.

+0

Grazie al fatto che 'construct <>' ha veramente tolto molti errori. Ora mi rimane solo uno: 'parser.hpp: 38: 81: errore: il valore di 'boost :: spirit :: _ 1' non è utilizzabile in un'espressione costante' e 'nota:' boost :: spirit :: _1 'non è stato dichiarato' constexpr''. Qualsiasi aiuto? – Lanbo

+0

Ok, ho sbagliato a scrivere la tua soluzione. Grazie! – Lanbo

+0

Quel link di Spirit Applications ha alcune ottime fonti di esempio, grazie! – rvalue