“Bison can create parsers in C++. Although flex appears to be able to create C++,scanners,the C++ code doesn’t work.[21] Fortunately,C scanners created by flex compile under C++ and it is not hard to use a flex C scanner with a bison C++ parser”. (Footnote [21]: “This is confirmed by the guy who wrote it. It will probably be fixed eventually,but it turned out to be surprisingly hard to design a good C++ interface for flex scanners.”)
在我努力编写一个相当复杂的Flex扫描程序之前,我(我想我们很多人)想知道自2009年以来是否有任何关于这方面的变化.是否有人成功编写Flex / C解析器?如果是这样,是否值得努力,或者C扫描仪是否仍然是最安全的C解析器?
解决方法
我可以向你揭示我编写的解析器的准确性,但这只是你如何做到这一点的一个例子.
请注意,这些代码中的一些是通过反复试验设置的,因为文档很少,所以可能是多余的操作或不完全正确但可行的事情.
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; }; }
最后一步是定义你的Compiler类,它将从Bison语法规则中调用(这就是ypp文件中parse-param属性的用途).就像是:
#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代码,例如:
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,以使其生成C词法分析器.
实际上,通过一些谨慎的操作,我也可以在同一个项目中拥有多个独立且可自我重入的词法分析器/解析器.