C ++ istream with lex

时间:2012-03-09 02:22:12

标签: c++ parsing lex lexer

我有一个工作语法(用lex和bison编写)来解析多项式表达式。这就像您的标准,教科书计算器般的语法。这是语法的一个非常简化的版本:

Expr
: DOUBLE        {$$ = newConstExpr($1);}
| Expr '+' Expr {$$ = newBinaryExpr('+', $1, $2);}
| Expr '*' Expr {$$ = NewBinaryExpr('*', $1, $2);}
| '(' Expr ')'  {$$ = $2;}
;

我的问题是Lex为yyin使用FILE *,我需要解析来自C ++ istream的输入。我知道flex ++可以生成FlexLexer类(可以在其构造中使用istream),但是很难让它与Bison相结合,甚至作者自己声称(在生成的lexer文件的注释中)它马车。

所以我想知道是否有人知道使用flex扫描程序和bison解析器与C ++ istream对象作为输入而不是FILE *的好方法。

2 个答案:

答案 0 :(得分:1)

您可以通过定义自定义YY_INPUT宏来获取lex的输入。

对于一个真实世界的例子,请看看我的:

http://www.kylheku.com/cgit/txr/tree/parser.l

在这里,我重定向flex扫描程序以处理作为动态对象库一部分的特殊流对象。与iostream一样,这些不是FILE *

这允许我在使用-c <script text>运行程序时执行词法分析命令行等操作。

(另外,扫描器使用8位字节。这就是YY_INPUT宏使用我的get_byte函数的原因。当yyin_stream是字符串流时,{{{实现将实际输出与字符串内的Unicode字符对应的UTF-8编码字节,因此在流前进到字符串的下一个字符之前可能需要多次get_byte调用。在文件流上,get_byte只从底层OS流中获取字节。)

答案 1 :(得分:0)

这是自定义YY_INPUT宏的工作示例,用于从交互式istream中读取。

%{
// Place this code in istr.l and run with:
// $ flex istr.l && c++ istr.cpp && ./a.out
// $ flex istr.l && c++ istr.cpp && ./a.out 1a2b 123 abc
#include <iostream>

// The stream the lexer will read from.
// Declared as an extern
extern std::istream *lexer_ins_;

// Define YY_INPUT to get from lexer_ins_
// This definition mirrors the functionality of the default
// interactive YY_INPUT
#define YY_INPUT(buf, result, max_size)  \
  result = 0; \
  while (1) { \
    int c = lexer_ins_->get(); \
    if (lexer_ins_->eof()) { \
      break; \
    } \
    buf[result++] = c; \
    if (result == max_size || c == '\n') { \
      break; \
    } \
  }

%}

/* Turn on all the warnings, don't call yywrap. */
%option warn nodefault noyywrap
/* stdinit not required - since using streams. */
%option nostdinit
%option outfile="istr.cpp"

%%
      /* Example rules. */
[0-9] { std::cout << 'd'; }
\n    { std::cout << std::endl; }
.     { std::cout << '.'; }
<<EOF>> { yyterminate(); }
%%

//
// Example main. This could be in its own file.
//
#include <sstream>

// Define actual lexer stream 
std::istream *lexer_ins_;

int main(int argc, char** argv) {
  if (argc == 1) {
    // Use stdin
    lexer_ins_ = &std::cin;
    yylex();
  } else {
    // Use a string stream
    std::string data;
    for (int n = 1; n < argc; n++) {
      data.append(argv[n]);
      data.append("\n");
    }
    lexer_ins_ = new std::istringstream(data);
    yylex();
  }
}

这种风格的扫描仪 - 使用C ++但是以C风格生成 - 对我来说很好。您也可以尝试实验性Flex选项%option c++。请参阅Flex手册中的“生成C ++扫描仪”。似乎没有太多关于将这些扫描仪与Bison解析器集成的信息。

最后,如果从内存中读取内容足以满足您的使用需求,您可以避免重新定义YY_INPUT - 请参阅Flex手册中的yy_scan_buffer()