【问题标题】:C++ istream with lexC++ istream 与 lex
【发布时间】:2012-03-09 02:22:11
【问题描述】:

我有一个解析多项式表达式的有效语法(用 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 啮合,甚至作者自己声称(在生成的词法分析器文件中的 cmets 中)它有问题。

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

【问题讨论】:

  • 你读过野牛手册中的example吗?还是otherexamplesgoogle 找到了?
  • 我看过其中的一些,发现它们没有帮助。不过,其中一个例子看起来很有希望。我会仔细看看的。谢谢。

标签: c++ parsing lex lexer


【解决方案1】:

您可以通过定义自定义 YY_INPUT 宏来随意输入 lex。

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

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

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

这让我可以在使用-c <script text> 运行程序时对命令行进行词法分析。

(顺便说一句,扫描器使用 8 位字节。这就是 YY_INPUT 宏使用我的 get_byte 函数的原因。当 yyin_stream 是字符串流时,get_byte 实现实际上会输出UTF-8 编码字节对应于字符串中的 Unicode 字符,因此在流前进到字符串的下一个字符之前可能需要多次调用get_byte。在文件流中,get_byte 只是从底层操作系统流。)

【讨论】:

  • 请注意YY_INPUT 在看到换行符时故意停止读取并返回部分填充的缓冲区。如果底层流是交互式的,这一点很重要。如果YY_INPUT 只是继续读取字符直到它填满缓冲区,它将撤消下面的交互式无缓冲或行缓冲流的效果,使其显示为缓冲!如果你想从 lex 获得即时的一次字符响应,你还必须重写 YY_INPUT 而不是缓冲行(除了从你的底层系统安排那种输入风格)。
【解决方案2】:

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

%{
// 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()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-24
    • 1970-01-01
    • 1970-01-01
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多