2011-12-10 24 views
8

Después de leer el tutorials en boost::spirit, me gustó bastante debido a la sintaxis del combinador del analizador. Hacer un analizador es tan fácil.Recuperando AST de boost :: spirit parser

Desafortunadamente, los tutoriales no fueron tan exactos en cuanto a la obtención de una estructura de datos compleja del analizador. Estoy tratando de llegar al Kaleidoscope AST.

De todos modos, aquí sea mi código de 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 

he omitido las partes BOOST_FUSION_ADAPT_STRUCT, pero están ahí.

Y esta mi analizador de expresiones:

#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 

El problema que tengo es que parece tener un problema con el resultado ast::expression. El compilado arroja más de 200 líneas de errores complejos de plantilla. Sospecho que es algo con la forma en que traté de obtener la información de la regla binop, pero no estoy seguro.

¿Alguien puede ayudar?

Respuesta

7

Está intentando llamar al constructor de ast::binary_op usando los marcadores de posición de Boost Phoenix. No se mezclan bien Debe utilizar un lazy call en el constructor ast::binary_op. Esto está previsto en Phoenix utilizando 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)] ; 

Además, creo que sólo necesita los _1 y _2 marcadores de posición, como el '+', '-', ... se convierten en qi::lit (Litteral) por lo tanto tener ningún atributo.

También he tomado nota de un par de paréntesis que falta en varname y number reglas:

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

Boost Espíritu Qi es muy potente, pero también muy difíciles de depurar. Cuando comencé a usarlo, encontré estos Boost Spirit Applications muy útiles.

Espero que esto sea útil, ya que no soy un experto en Boost Spirit.

+0

Gracias que 'construct <>' realmente eliminó muchos errores. Ahora solo me queda uno: 'parser.hpp: 38: 81: error: el valor de 'boost :: spirit :: _ 1' no se puede usar en una expresión constante' y' nota :: 'boost :: spirit :: _1 'no fue declarado' constexpr''. ¿Alguna ayuda? – Lanbo

+0

Ok, deseche eso, cometí un error al escribir su solución. ¡Gracias! – Lanbo

+0

El enlace de Aplicaciones de Spirit tiene algunas excelentes fuentes de ejemplos, ¡gracias! – rvalue

Cuestiones relacionadas