C++ 有限状态机解析器

C++ 有限状态机解析器,c++,parsing,stream,fsm,C++,Parsing,Stream,Fsm,我想用类似FSM的解析器在C++中解析一个自行设计的文件格式(这是一个自学C++-通过做一些大而难的项目:))。我有一个标记化字符串,带有新行,表示euh的结束。。。线路。看见所有的评论都会被过滤掉,垃圾也会被过滤掉,所以我有一个std::字符串,如下所示: 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

我想用类似FSM的解析器在C++中解析一个自行设计的文件格式(这是一个
自学C++-通过做一些大而难的项目:))。我有一个标记化字符串,带有新行,表示euh的结束。。。线路。看见所有的评论都会被过滤掉,垃圾也会被过滤掉,所以我有一个std::字符串,如下所示:

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对于我的需求/知识来说足够简单/可扩展。据我所知(并希望我的文件设计是),我不需要并发状态或任何类似的花哨的东西。一些设计/实施问题:

  • 我应该为我的州使用
    枚举
    还是抽象的
    +派生?第一种可能更适合小语法,但以后可能会变得难看,而第二种恰恰相反。我倾向于第一种,因为它简单。和。编辑:对于<代码> Goto < /C>,我认为它们是C++中的邪恶?
  • 阅读列表时,我不需要忽略
    \n
    。默认情况下,我通过
    stringstream
    使用
    字符串的首选方式将忽略
    \n
    。因此,我需要一种简单的方法来告诉(同样!)
    stringstream
    在启用某个状态时不要忽略换行符
  • 简单的
    enum
    状态是否足以进行多级解析(scopes
    {…{…}}}
    中的scopes),还是需要hacky实现
  • 以下是我心目中的草案状态:
    • upper
      :读取全局、exe、lib+目标名称
    • normal
      :在范围内,可以读取源…,创建用户变量
    • 列表
      :将项目添加到列表中,直到遇到换行符
  • 每个作用域都有一种条件(例如win32:global{gcc:CFLAGS=…}),并且需要在任何地方以完全相同的方式进行处理(即使在
    列表
    状态下,每个项)


    感谢您的输入。

    如果您有嵌套作用域,那么有限状态机不是正确的选择,您应该看看上下文无关的语法分析器。可以将编写为一组递归函数,也可以使用解析器生成器(如Bison)编写

    如果你向FSM添加一个堆栈,那么你就进入了一个领域。非确定性下推自动机相当于上下文无关语法(尽管A的功能严格来说不太强大)。LALR(1)解析器生成器实际上在内部生成确定性下推自动机。一本好的编译器设计教科书将涵盖从语法构造下推自动机的精确算法。(通过这种方式,添加堆栈并不是“黑客式的”。)还描述了如何从语法中构造LR(1)下推自动机,但在我看来,这篇文章并没有那么清晰


    如果您的作用域嵌套深度有限(即您有
    上部
    正常
    列表
    级别,但没有嵌套的
    列表
    或嵌套的
    正常
    s),然后,您可以使用不带堆栈的FSM。

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


    <>当然,如果你发现或者更容易使用,你也可以使用它们(我知道它们只生成C和C++,但是我可能是错误的,因为我在一段时间内没有使用它们)。 词法分析:这是将输入流分解为词法单元的地方。它查看字符序列并生成标记(与口语或书面语言中的单词相似)。如果您对词法结构做出了良好的设计决策,那么有限状态机非常擅长词法分析。从上面的数据来看,单个词素可能是关键字(例如“全局”)、标识符(例如“按位”、“源”)、符号词素(例如“{”“}”、““,”/”)、数值、转义值(例如“\n”)等

    句法/语法分析:在生成一个标记序列时(或者在生成过程中),您需要能够分析结构,以确定标记序列是否与您的语言设计一致。通常需要某种类型的解析器来实现这一点,不过如果语言结构不是很复杂,可以使用有限状态机来实现。一般来说(由于您特别需要嵌套结构),您需要使用Ken Bloom描述的技术之一

    因此,在回答您的问题时:

    我应该为我的状态使用枚举还是抽象类+导数

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

    阅读列表时,我不需要忽略\n

    您可以创建一个名为“\n”的令牌,也可以创建一个更通用的转义令牌(一个前面有反斜杠的标识符。如果您正在谈论id