我正在编写一个程序,以自定义格式输入直接播放,然后对其进行一些分析(如每个字符的行数和单词数)。它只是为了好玩,是学习很酷的东西的借口。 该过程的第一步是为该格式编写解析器。它是:
####Play
###Act I
##Scene 1
CHARACTER 1. Line 1, he's saying some stuff.
#Comment, stage direction
CHARACTER 2, doing some stuff. Line 2, she's saying some stuff too.
这是一种非常简单的格式。我广泛阅读了关于CFG等基本解析器的内容,所以我现在准备完成一些工作了。
我已经在EBNF中编写了我的语法并开始使用flex / bison但它提出了一些问题:
这让我很困惑。我正在寻找一个优雅,或许简单的解决方案。任何指南?
顺便说一下,关于编程语言,我并不在意。现在我因为flex / bison而使用C,但只要它是一种广泛使用的语言,我就可以随意告诉我更实用的东西。
答案 0 :(得分:2)
在不知道解析期望是什么的情况下回答这些问题非常困难。也就是说,几行文本的例子并没有清楚地理解预期的解析是什么;词汇和句法单位是什么;你想提取什么样的关系;等等。
然而,一个粗略的猜测可能是您打算生成嵌套解析,其中##{i}
指示嵌套级别(反向),i≥1
,因为单个#
不是结构。这违反了语言设计的一个原则(“不要让用户计算计算机可以更准确计算的东西”),这可能表明结构更像是:
@play {
@act {
@scene {
@location: Elsinore. A platform before the castle.
@direction: FRANCISCO at his post. Enter to him BERNARDO
BERNARDO: Who's there?
FRANCISCO: Nay, answer me: stand, and unfold yourself.
BERNARDO: Long live the king!
FRANCISCO: Bernardo?
甚至是类似XML的东西。但那将是一种不同的语言:)
使用经典扫描器/解析器组合解析其中任何一个的问题是词法结构不一致;一行中的第一个标记是特殊的,但大部分文件都包含未解析的文本。这几乎不可避免地导致在扫描程序和解析器之间传播语法信息,因为扫描程序需要知道语法上下文以确定它是否正在扫描原始文本。
您可以避免该问题。例如,您可能要求延续行以空格开头,以便以#
标记的每一行都以字符的名称开头。这比识别对话线更可靠,因为它以字符和句点的名称开头,因为字符的名字很可能用于对话,即使在句子结尾处(因此可能是延续线中的第一个单词。)
如果您打算通过以字符名称和一些标点符号开头来区分对话行,那么您肯定必须让扫描程序访问字符列表(作为一种符号表),是一个众所周知但不是特别受人尊敬的黑客。
考虑上面关于你的第二个问题的反思(“扫描仪和解析器的角色是什么?”),这不符合答案,但希望至少可以考虑。至于你的其他问题,并认识到所有这些都是自以为是:
对于这样一个简单的解析器来说,flex / bison太多了吗?我应该自己写一下......
flex和bison(可能)比解析特定语言所需的功能更强大这一事实是一个红色的鲱鱼。 C编写阶乘函数比编写函数更有用 - 你可以在汇编程序中轻松完成 - 但是编写阶乘函数是学习C的一个很好的练习。同样,如果你想学习如何编写解析器,这是一个很好的想法从简单的语言开始;很明显,这不会解析解析器/扫描器生成器中的每个选项,但它会让你开始。问题在于你所设计的语言是否适合这种解析方式,而不是它是否过于简单。
使用flex / bison,在解析时执行分析是否有意义,或者首先解析是否更优雅,然后使用其他工具再次对文件进行操作?
要么是优雅的,要么是灾难性的;优雅更多地与你如何构建对手头问题的思考结构有关。话虽如此,通常最好在解析阶段构建语义结构(通常称为AST - 抽象语法树),然后使用其他函数分析该结构。
重新扫描输入文件不太可能优雅或有效。