Interpreterbau - Parsen



  • Möp!

    Ich habe einen Lexer für meine Programmiersprache mithilfe von boost gebaut. Parsen geschieht danach ja über spirit.qi. Wie wird aber aus einem Literal-Token (int, double, ...) dann wirklich der entsprechende Typ? Ich fange mit spirit.qi nicht wirklich was an, hier gibt man eine EBNF, und die wird dann validiert. Fehlermeldungen kann man da auch schlecht basteln. Und der AST geht mir völlig ab.

    Ich steh auf dem Schlauch, kann mir jemand erklären, wie das funktioniert?

    Grüßle, PI



  • Ich würde den Parser selber schreiben, das ist IMO bei den ersten paar einfacher als über Compiler-Compiler.



  • Für übliche Umwandlungen sollte es reichen, boost::spirit::lex::token_def den entsprechenden Typ als Attribut zu geben. Einfaches Beispiel (naja, so einfach Parser-Beispiele halt werden):

    #include <boost/spirit/include/lex.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/lex_lexertl.hpp>
    
    #include <algorithm>
    #include <iostream>
    #include <iterator>
    #include <string>
    #include <vector>
    
    namespace qi = boost::spirit::qi;
    namespace lex = boost::spirit::lex;
    
    template<typename Lexer>
    struct num_lexer : lex::lexer<Lexer> {
      num_lexer() {
        number = "[0-9]+";
    
        this->self = number;
        this->self("WS") = "[ \\t\\n]+";
      }
    
      lex::token_def<int> number; // <--- Hier
    };
    
    template<typename iterator_t, typename Lexer>
    struct num_grammar : qi::grammar<iterator_t, std::vector<int>(), qi::in_state_skipper<Lexer> > {
      template<typename TokenDef>
      num_grammar(TokenDef const &tok) : num_grammar::base_type(sequence) {
        sequence %= *tok.number;
      }
    
      qi::rule<iterator_t, std::vector<int>(), qi::in_state_skipper<Lexer> > sequence;
    };
    
    int main() {
      typedef std::string::iterator base_iterator_type;
      typedef lex::lexertl::token<base_iterator_type, boost::mpl::vector<int> > token_type;
      typedef lex::lexertl::lexer<token_type> lexer_type;
      typedef num_lexer<lexer_type> num_lexer_t;
      typedef num_lexer_t::iterator_type iterator_type;
      typedef num_grammar<iterator_type, num_lexer_t::lexer_def> num_grammar_t;
    
      std::string str = "1 2 34 567 8 90";
    
      num_lexer_t   lexer;
      num_grammar_t grammar(lexer);
    
      base_iterator_type str_iter = str.begin();
      num_lexer_t::iterator_type tok_iter = lexer.begin(str_iter, str.end());
    
      std::vector<int> result;
    
      if(qi::phrase_parse(tok_iter, lexer.end(), grammar, qi::in_state("WS")[lexer.self], result)) {
        std::copy(result.begin(), result.end(), std::ostream_iterator<int>(std::cout, "\n"));
      }
    }
    


  • Soll ich auf fdfdg's Rat hören oder ist es einfacher mit spirit.qi?



  • 314159265358979 schrieb:

    Soll ich auf fdfdg's Rat hören oder ist es einfacher mit spirit.qi?

    Recursive Descent Parser sind relativ einfach zu schreiben, solange man weißt wie man Follow- und -First-Mengen errechnet und dabei beachtet, dass man seine Grammatik durch Linksrekursion und Faktorisierung umformen muss.



  • Sollte mich die Tatsache, dass ich keinen deiner Fachbegriffe kenne, beunruhigen? 😃



  • S := A | B
    A := 'a'
    B := 'a'
    

    Grammatik mehrdeutig, so implementiert, macht der Parser nur misst.

    First(S) = First(A) + First(B) = {'a', 'a'} // ops
    First(A) = {'a'}
    First(B) = {'a'}

    First Mengen kann man auch sehen ;), und ops Follow Mengen braucht man nicht für RDP.


Log in to reply