2016-04-16 6 views
5

das Buch Zitiert flex & Bison (O'Reilly, John Levine, 2009).Funktioniert GNU/Flex C++ überhaupt?

„Bison kann Parser in C++ erstellen Obwohl flex scheint in der Lage zu sein, C++ zu erstellen, Scanner, die C++ - Code funktioniert nicht. [21] Glücklicherweise kompilieren C-Scanner, die mit flex erstellt wurden, unter C++, und es ist nicht schwer, einen Flex C-Scanner mit einem Bison-C++ - Parser zu verwenden ". (Fußnote [21]: „Dies wird durch den Typ bestätigt, die es geschrieben Es wird wahrscheinlich schließlich fixiert werden, aber es stellte sich heraus, überraschend schwer zu sein, eine gute C++ Schnittstelle für flex-Scanner zu entwerfen..“)

Bevor ich mich anschreibe, einen ziemlich komplexen Flex-Scanner zu schreiben, würde ich (und ich glaube, viele von uns) gerne wissen, ob sich irgendetwas darüber seit 2009 geändert hat. Gibt es jemanden, der erfolgreich Flex/C++ Parser schreibt? Wenn ja, ist es die Mühe wert oder ist ein C Scanner mit einem C++ Parser immer noch der sicherste Kurs?

+0

Vollständig erklärt hier: https: // bitbucket.org/emmetac/miscellany/src/master/flex/bison-cxx-beispiel/ – Emmet

+0

Ich benutzte es genau vor 25 Jahren für genau diesen Zweck. Natürlich funktioniert es. – EJP

Antwort

2

Es ist völlig möglich und es funktioniert gut einmal eingerichtet. Leider ist die Dokumentation über den reinen C++ Flex/Bison Lexer-Parser nicht so einfach zu finden und zu folgen.

Ich kann Ihnen ein Barebone eines Parsers aussetzen, den ich geschrieben habe, aber es ist nur ein Beispiel dafür, wie Sie es tun könnten.

Beachten Sie, dass ein Teil dieses Codes durch Versuch und Irrtum eingerichtet wurde, da Dokumentation selten ist, so dass es überflüssige Operationen oder Dinge, die nicht genau richtig sind, aber es funktioniert.

YPP Datei

%skeleton "lalr1.cc" 
%require "3.0.2" 

%defines 
%define api.namespace {script} 
%define parser_class_name {Parser} 

%define api.token.constructor 
%define api.value.type variant 
%define parse.assert true 

%code requires { 

    namespace script 
    { 
    class Compiler; 
    class Lexer; 
    } 
} 

%lex-param { script::Lexer &lexer } 
%lex-param { script::Compiler &compiler } 
%parse-param { script::Lexer &lexer } 
%parse-param { script::Compiler &compiler } 

%locations 
%initial-action 
{ 
    @$.begin.filename = @$.end.filename = &compiler.file; 
}; 

%define parse.trace 
%define parse.error verbose 

%code top { 
    #include "Compiler.h" 
    #include "MyLexer.h" 
    #include "MyParser.hpp" 

    static script::Parser::symbol_type yylex(script::Lexer &scanner, script::Compiler &compiler) { 
    return scanner.get_next_token(); 
    } 

    using namespace script; 
} 

// tokens and grammar 

void script::Parser::error(const location_type& l, const std::string& m) 
{ 
    compiler.error(l,m); 
} 

Hier können Sie C++ verwenden, überall, zum Beispiel

%type<std::list<Statement*>> statement_list for_statement 
... 
statement_list: 
    { $$ = std::list<Statement*>(); } 
    | statement_list statement { $1.push_back($2); $$ = $1; } 
; 

l Datei

%{ 
    #include "MyParser.hpp" 
    #include "MyLexer.h" 
    #include "Compiler.h" 
    #include <string> 

    typedef script::Parser::token token; 

    #define yyterminate() script::Parser::make_END(loc); 

    static script::location loc; 

    using namespace script; 
%} 

%x sstring 
%x scomment 

%option nodefault 
%option noyywrap 
%option c++ 
%option yyclass="Lexer" 
%option prefix="My" 


%{ 
    # define YY_USER_ACTION loc.columns((int)yyleng); 
%} 


%% 

%{ 
    loc.step(); 
%} 

Dann Sie eine Header-Datei benötigen, die definiert, Ihre Lexer Klasse, die von erben wird, dass ich s, wie C++ Flex arbeitet, die so etwas wie

#if ! defined(yyFlexLexerOnce) 
#undef yyFlexLexer 
#define yyFlexLexer NanoFlexLexer 
#include <FlexLexer.h> 
#endif 

#undef YY_DECL 
#define YY_DECL script::Parser::symbol_type script::Lexer::get_next_token() 

#include "MyParser.hpp" 

namespace script 
{ 
    class Compiler; 

    class Lexer : public yyFlexLexer 
    { 
    public: 

    Lexer(Compiler &compiler, std::istream *in) : yyFlexLexer(in), compiler(compiler) {} 

    virtual script::Parser::symbol_type get_next_token(); 
    virtual ~Lexer() { } 

    private: 

    Compiler &compiler; 
    }; 

} 

Der letzte Schritt Ihre Compiler-Klasse ist die Definition, die von den Bison Grammatikregeln genannt wird erhalten (das ist, was parse-param Attribute in YPP-Datei sind für). Etwas wie:

#include "parser/MyParser.hpp" 
#include "parser/MyLexer.h" 
#include "parser/location.hh" 

#include "Symbols.h" 

namespace script 
{ 
    class Compiler 
    { 

    public: 
    Compiler(); 

    std::string file; 

    void error(const location& l, const std::string& m); 
    void error(const std::string& m); 

    vm::Script* compile(const std::string& text); 

    bool parseString(const std::string& text); 

    void setRoot(ASTRoot* root); 
    Node* getRoot() { return root.get(); } 
    }; 
} 

Jetzt können Sie execute Parsen leicht und vollständig vorbei C++ Code, zB:

bool Compiler::parseString(const std::string &text) 
{  
    constexpr bool shouldGenerateTrace = false; 

    istringstream ss(text); 

    script::Lexer lexer = script::Lexer(*this, &ss); 
    script::Parser parser(lexer, *this); 
    parser.set_debug_level(shouldGenerateTrace); 
    return parser.parse() == 0; 
} 

Das einzige, was man darauf achten muss, ist flex auf der .l Datei mit -c++ Argument aufzurufen damit es einen C++ - Lexer erzeugt.

Eigentlich mit einigen sorgfältigen Operationen konnte ich auch mehrere unabhängige und selbstregulierende Lexer/Parser im selben Projekt haben.

+0

Danke an alle. Ich mag besonders das Wort *** reentrant *** in einer der Antworten. Ich gebe dem C++ Lexer eine Chance. – user3513432

Verwandte Themen