如何在解析器和词法分析器之间分离职责以匹配文本块?

时间:2011-05-31 11:44:18

标签: bison yacc lex flex-lexer

我有一个我正在尝试解析的文本文件。该文件如下所示:

A_1: - A_10:
Some text.
----------
Some more text.
__________

B_1: - B_5:
Still more text - it may contain dashes as well.
----------
Even more text. Could be multiple sentences with newlines.
Like this.
__________

等等。

我正在尝试将bisonflex之间的解析/标记分开。我已设法使用A_1: - A_10:中的以下正则表达式解析标题(flex):

[ \t]+ ;               // ignore whitespace
[A-Z]_[0-9]+(_[0-9]+)? { ... return ID; }

结合我的语法规则来组合两个ID:

header:        ID ':' '-' ID ':'

但是,下一段文字会造成一些麻烦。我很确定我需要在词法分析器中包含启动条件(例如,在解析头时只忽略空格)。我试图定义一个令牌TEXT并解析整个事件直到----------作为单个令牌。仍然无法弄清楚这是否合理。

我能想到的另一种可能性是在语法中有一个规则,它将使用WORD,SPACE,DASH,NEWLINE等标记和其他所有可能的字符组合文本段。它甚至有意义吗?

所以现在我不得不试图解析那些文本段。我是否使用合适的工具来完成工作。感谢您的帮助,谢谢。

2 个答案:

答案 0 :(得分:3)

这是lex开始状态的目的。基本上,您为需要处理的每种不同语言声明一个开始状态(在您的情况下为两个 - 标题和正文),然后根据它们应用的状态标记规则。所以你想要的东西是:

%s header
%s body
%%
<header>[ \t\n]                   ; /* ignore */
<header>[_a-zA-Z][_a-zA-Z0-9]*    { ... return ID; }
<header>[-:,.;()]                 { return *yytext; }

<body>^----------$                { yylval.text = GetStoredText(); return SECTION_SPLIT; }
<body>^__________$                { yylval.text = GetStoredText(); return SECTION_END; }
<body>.                           { StoreText(*yytext);
%%
void BeginHeader() { BEGIN header; }
void BeginBody() { BEGIN body; }

其中StoreText是一个将字符存储到缓冲区的函数(如果你使用的是C ++,则类似于std :: stringstream),GetStoredText返回自上次调用以来存储的所有文本并清除缓冲区。然后您的yacc / bison代码将类似于:

input: entry | input entry ;
entry: header body ;
header: ..something to match a header.. { BeginBody(); };
body: sections SECTION_END { BeginHeader(); };
sections: /*empty*/ | sections SECTION_SPLIT ;

当然,您还希望代码能够根据正文部分的内容做任何您想做的事情......

答案 1 :(得分:0)

我已经意识到逐行处理这样的文档(即编写适合该任务的自己的解析器)可能会产生更清晰的解决方案。

另一种可能性是在每个__________标记处拆分整个文件。通过这种方式,我们将获得许多部分。然后将每个部分拆分为----------个标记。现在我们能够提取当前部分的第二个文本块(“更多文本。”在上面的例子中的第一部分)。第一个文本块只是一行标题,后跟“Some text。”直到chunk结束。

这种算法很容易用Perl,Python,Ruby等脚本语言实现。