use*_*432 5 c++ bison flex-lexer
引用本书flex&bison(O'Reilly,John Levine,2009):
"Bison可以用C++创建解析器.虽然flex似乎能够创建C++,但扫描程序,C++代码不起作用.[21]幸运的是,在C++下通过flex编译创建的C扫描程序并不难使用flex带有野牛C++解析器的C扫描器".(脚注[21]:"这是由编写它的人证实的.它最终可能会被修复,但结果却很难为柔性扫描仪设计一个好的C++接口.")
在我努力编写一个相当复杂的Flex扫描程序之前,我(我想我们很多人)想知道自2009年以来是否有任何关于这方面的变化.有没有人成功编写Flex/C++解析器?如果是这样,是否值得努力,或者C扫描仪是否仍然是最安全的C扫描仪?
这是完全可能的,一旦设置就很好用。不幸的是,关于纯 C++ Flex/Bison 词法分析器的文档并不容易找到和遵循。
我可以向您展示我编写的解析器的准系统,但这只是您如何做到的一个示例。
请注意,其中一些代码是通过反复试验设置的,因为文档很少,所以它们可能是多余的操作或不完全正确的东西,但它可以工作。
%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类,它将从yyFlexLexerC++ 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 语法规则中调用(这就是parse-paramypp 文件中的属性)。就像是:
#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;
}
你必须小心的唯一一件事就是调用flex上.l与文件-c++的说法,使之产生一个C ++词法分析器。
实际上,通过一些谨慎的操作,我还能够在同一个项目中拥有多个独立且自重入的词法分析器/解析器。
| 归档时间: | 
 | 
| 查看次数: | 961 次 | 
| 最近记录: |