有限状态机解析器

时间:2010-06-21 13:31:42

标签: c++ parsing stream fsm

我想用C ++中的类似FSM的解析器来解析自行设计的文件格式(这是一种teach-myself-c++-the-hard-way-by-doing-something-big-and-difficult项目:))。我有一个带有换行符的标记化字符串,表示euh ...行的结束。见here for an input example。所有的评论和垃圾都会被过滤掉,所以我有一个像这样的std :: string:

global \n { \n SOURCE_DIRS src \n HEADER_DIRS include \n SOURCES bitwise.c framing.c \n HEADERS ogg/os_types.h ogg/ogg.h \n } \n ...

语法说明:

  • {}是范围,大写单词表示将遵循选项/文件列表。
  • \ n仅在选项/文件列表中很重要,表示列表的末尾。

所以我认为FSM对我的需求/知识来说足够简单/可扩展。据我所知(并希望我的文件设计),我不需要并发状态或任何类似的东西。一些设计/实施问题:

  1. 我应该为我的州使用enum或抽象的class +衍生物吗?第一个可能更适合小语法,但后来可能会变得丑陋,第二个恰恰相反。因为它的简单,我倾向于第一个。 enum exampleclass example。编辑:goto的{​​{3}}怎么样,我认为它们在C ++中是邪恶的?
  2. 阅读列表时,我不需要忽略\n。我通过string使用stringstream的首选方式默认会忽略\n。所以我需要简单的方式告诉(同样!)stringstream在启用某个状态时不要忽略换行符。
  3. 简单enum状态是否足以进行多级解析(范围{...{...}...}内的范围)或需要hacky实现?
  4. 这是我想到的草案:
    • upper:读取全局,exe,lib +目标名称......
    • normal:在范围内,可以读取SOURCES ...,创建用户变量......
    • list:将项目添加到列表中,直到遇到换行符。
  5. 每个范围都有一种条件(例如win32:global {gcc:CFLAGS = ...}),需要以完全相同的方式处理(即使在list状态,每个项)。

    感谢您的任何意见。

3 个答案:

答案 0 :(得分:12)

如果你有嵌套作用域,那么有限状态机是正确的方法,你应该看一下Context Free Grammar解析器。 LL(1) parser可以写成一组递归函数,或者LALR(1) parser可以使用像Bison这样的解析器生成器编写。

如果您向FSM添加堆栈,那么您将进入pushdown automaton区域。非确定性下推自动机等同于无上下文语法(尽管deterministic pushdown automaton严格地说不那么强大。)LALR(1)解析器生成器实际上在内部生成确定性下推自动机。一个好的编译器设计教科书将涵盖从语法构造下推自动机的确切算法。 (通过这种方式,添加一个堆栈不是“hacky”。​​)This Wikipedia article还描述了如何从你的语法构造LR(1)下推自动机,但IMO,文章并不像它可能那样清晰。

如果您的范围仅有限地嵌套(即您具有uppernormallist级别,但您没有嵌套list s或嵌套{{ 1)} s),然后你可以使用没有堆栈的FSM。

答案 1 :(得分:3)

分析用于解析的文本输入流有两个阶段:

词法分析:这是您的输入流被分解为词汇单位的地方。它查看一系列字符并生成令牌(与口头或书面语言中的单词类似)。如果您对词法结构做出了很好的设计决策,那么有限状态机非常擅长词法分析。根据您的上述数据,个别词位将是您的关键词(例如“全局”),标识符(例如“按位”,“来源”),符号tokesn(例如“{”“}”,“。”,“/” ),数值,转义值(例如“\ n”)等

句法/语法分析:生成一系列令牌(或者在您这样做时),您需要能够分析结构以确定令牌序列是否与你的语言设计。您通常需要某种解析器,但如果语言结构不是很复杂,您可以使用有限状态机来代替它。一般而言(因为你特别需要在你的情况下使用嵌套结构),你需要使用Ken Bloom描述的技术之一。

所以回答你的问题:

我应该为我的州使用枚举或抽象类+衍生物吗?

我发现对于小型标记化器,状态/转移值矩阵是合适的,类似于next_state = state_transitions[current_state][current_input_char]。在这种情况下,next_statecurrent_state是一些整数类型(可能包括枚举类型)。转换到无效状态时会检测到输入错误。基于有效状态的状态标识来识别令牌的结束,在给定下一个输入字符的情况下,没有有效转换可用于另一状态。如果您担心空间,可以使用地图矢量代替。制作州级课程是可能的,但我认为这可能使事情变得比你需要的更困难。

阅读清单时,我不要忽视\ n。

你可以创建一个名为“\ n”的标记,或者一个更通用的转义标记(一个前面带有反斜杠的标识符。如果你在谈论识别源代码中的换行符,那么这些只是你需要的字符在状态转换矩阵中创建转换(但要注意Unix和Windows换行符之间的差异;您可以创建一个可在其上运行的FSM)。

简单的枚举状态是否足以进行多级解析(范围{... {...} ...}中的范围)还是需要hacky实现?

这是您需要语法或下推自动机的地方,除非您可以保证嵌套不会超过某个级别。即使这样,它也可能使您的FSM变得非常复杂。

以下是我想到的草案:......

请参阅上面关于词汇和语法分析的文章。

答案 2 :(得分:1)

对于解析,我总是尝试使用已证明可行的东西:ANTLRANTLRWorks,这对设计和测试语法有很大帮助。您可以为C / C ++(和other languages)生成代码,但是您需要为这些语言构建ANTLR运行时。

当然如果您发现flexbison更容易使用,您也可以使用它们(我知道它们只生成C和C ++但我可能错了,因为我没有使用它们一段时间)。