2016-04-16 11 views
5

ブックフレックス&バイソン(オライリー、ジョン・レヴァイン、2009)引用:フレックスが表示されますが、C++、スキャナを作成できるようにするが、GNU/Flex C++はまったく動作しますか?

「バイソンは、C++でパーサーを作成することができますが、 C++コードは動作しません。[21]幸運なことに、C++で作成されたCのスキャナはC++でコンパイルされ、Bison C++パーサーではフレックスCスキャナを使用するのが難しくありません。 (脚注[21]:「。これは、それはおそらく最終的に固定され、それを書いた人によって確認されたが、それはフレックススキャナの良いC++インターフェースを設計するために、驚くほど困難であることが判明している」)

私がかなり複雑なFlexスキャナを書く努力をする前に、私は多くの人が2009年以来これについて何か変わったかどうかを知りたいと思います。誰かがFlex/C++パーサーを作成していますか?もしそうなら、それは努力する価値がありますか?Cスキャナで、C++パーサーが最も安全なコースですか?

+0

ここで詳しく説明します:https:// bitbucket。org/emmetac/miscellany/src/master/flex/bison-cxx-example/ – Emmet

+0

私は25年前にその目的のために使っていました。もちろんそれは動作します。 – EJP

答えて

2

これは完全に可能で、セットアップ後にはうまくいきます。残念なことに、純粋なC++ Flex/Bisonレクサーパーサーに関するドキュメントは、それほど簡単ではありません。

私が書いたパーサーの裸足を公開することができますが、それはあなたがそれを行う方法の一例に過ぎません。

このコードの一部は、ドキュメントが不足しているため、試行錯誤によってセットアップされていることに注意してください。余計な操作や正確ではないものの、動作する可能性があります。ここで

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

あなたはCを使用することができ++どこでも、たとえば

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

リットルファイル

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

のために、あなたはあなたを定義するヘッダファイルが必要になりますLexerクラスは、yyFlexLexerから継承します。 C++ Flexは

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

} 

最後のステップは、(それがYPPファイル内parse-param属性がのためにあるものだ)のBison文法規則から呼び出されます、あなたのコンパイラのクラスを定義しているようなものがある、どのように動作します。ような何か:

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

あなたが世話をしなければならない唯一のことは、-c++引数で.lファイルにflexを呼び出すことです:

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

今、あなたは例えば、C++コードで渡し、容易かつ完全に解析し、実行することができますそれがC++レクサーを生成するようにします。

実際にはいくつかの慎重な操作で、私は同じプロジェクト内に複数の独立した自己復帰型のレクサー/パーサを持つこともできました。

+0

ありがとうございます。私は答えの一つに***リエントラント***という言葉が特に好きです。私はC + +レクサーに行くつもりです。 – user3513432

関連する問題