これは完全に可能で、セットアップ後にはうまくいきます。残念なことに、純粋な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++レクサーを生成するようにします。
実際にはいくつかの慎重な操作で、私は同じプロジェクト内に複数の独立した自己復帰型のレクサー/パーサを持つこともできました。
ここで詳しく説明します:https:// bitbucket。org/emmetac/miscellany/src/master/flex/bison-cxx-example/ – Emmet
私は25年前にその目的のために使っていました。もちろんそれは動作します。 – EJP