2016-04-16 11 views
5

Cytując książkę Flex & bizon (O'Reilly, John Levine, 2009).Czy działa GNU/Flex C++?

„Bison mogą tworzyć parsery w C++ Chociaż pojawia się wygiąć, aby móc tworzyć C++, skanery, tym Kod C++ nie działa. [21] Na szczęście skanery C stworzone przez flex kompilują się pod C++ i nie jest trudno używać skanera Flex C z parserem żubra C++ ". (Przypis [21]: „Jest to potwierdzone przez faceta, który ją napisał prawdopodobnie będzie ustalony ostatecznie, ale okazało się być zaskakująco trudno zaprojektować dobrą C++ interfejs typu flex skanerów.”.)

Zanim zdecyduję się na napisanie dość złożonego skanera Flex, ja (i myślę, że wielu z nas) chciałbym się dowiedzieć, czy coś się zmieniło od 2009 roku. Czy ktoś tam skutecznie pisze parsery Flex/C++? Jeśli tak, czy warto to zrobić, czy skaner jest jeszcze bezpieczniejszy z parserem C++?

+0

Pełne wyjaśnienie tutaj: https: // bitbucket.org/emmetac/miscellany/src/master/flex/bison-cxx-example/ – Emmet

+0

Używałem go dokładnie w tym celu 25 lat temu. Oczywiście, że działa. – EJP

Odpowiedz

2

Jest to całkowicie możliwe i działa po skonfigurowaniu. Niestety, dokumentacja dotycząca czystego parseru C++ Flex/Bison lexer nie jest tak łatwa do znalezienia i śledzenia.

Mogę narazić Cię na brak parsera, który napisałem, ale to tylko przykład tego, jak możesz to zrobić.

Należy pamiętać, że część tego kodu została ustawiona metodą prób i błędów, ponieważ dokumentacja jest skąpa, więc mogą istnieć niepotrzebne operacje lub rzeczy, które nie są dokładnie poprawne, ale działa.

plik YPP

%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); 
} 

Tutaj można użyć C++ wszędzie, na przykład

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

plik l

%{ 
    #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(); 
%} 

Następnie trzeba plik nagłówka, który definiuje swojej Lexer klasa, która odziedziczy po yyFlexLexer, która s jak C++ działa Flex, który jest czymś w rodzaju

#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; 
    }; 

} 

Ostatnim krokiem jest określenie swoją klasę Compiler który dostanie nazwie od reguł gramatycznych Bison (to co parse-param atrybuty pliku YPP są przeznaczone). Coś jak:

#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(); } 
    }; 
} 

Teraz można wykonać analizowania łatwo i całkowicie przechodzi kodu C++, np:

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; 
} 

Jedyną rzeczą, którą musisz zadbać to, aby wywołać flex plik .l z -c++ argumentu sprawić, by produkował lexer C++.

Właściwie dzięki pewnym starannym operacjom mogłem także mieć wiele niezależnych i samorentujących się lekserów/analizatorów w tym samym projekcie.

+0

Dziękuję wszystkim. Szczególnie podoba mi się słowo *** Reentrant *** w jednej z odpowiedzi. Dam lexer C++. – user3513432