词汇和语法分析器软件

Lexical and syntax analyzer software

本文关键字:分析器 软件 语法分析 语法 词汇      更新时间:2023-10-16

我正在设计一种基于CSS ish(CSS+自定义扩展)的自定义语言,它基本上是这样工作的:

[object.member.value = 5]{
object.member.anothervalue:8 
object.member.yetanothervalue:'hello'
object.member.yetyetanothervalue.anothervalue:blue
}

基本上,该语言允许检查一些条件(如果可以嵌套),然后将一些值应用于对象。没有循环。这将存储在纯文本文件中,并在启动时加载到应用程序(C++)中。

我们的想法是将这个CSS ish文件翻译成C++树或类似的东西,可以在运行时进行评估

我正在考虑使用一些词法分析器和标记器(Yacc、Flex、Bison等)

你对要使用的工具/库有什么建议?

如果您希望不止一次这样做,请学习如何使用解析器生成器。从长远来看,这将为你省去很多痛苦。

从简单开始。这些工具会为你做很多事情,而且通常只需付出很少的努力。让他们这么做吧。在你尝试做复杂的事情之前,先把事情做好。

其余部分假设您将使用flexbison(它们与lexyacc类似);有很多选择。如果你决定尝试另一种选择,忽略这个答案的其余部分。

flexbison是坚固、维护良好、调试良好的软件包,包含大量文档,并且在很长一段时间内被广泛使用。请先阅读文档。

  • flex将自动从标准输入或提供的文件描述符中读取。让它这么做吧
  • flex将为您跟踪行号。让它这么做吧
  • bison会自动为您生成代币编号。让它这么做吧
  • CCD_ 10和CCD_。您不仅不需要提供令牌编号,甚至不需要提供代币名称。在您的flex文件中,只需将其放在末尾或末尾附近:

    . { return yytext[0]; }
    

    并且不用麻烦编写规则来处理单字符令牌。不要担心你会标记非法字符;bison将为您生成一条错误消息。

  • 但是,不允许flex插入默认规则。(%option nodefault足以抑制它。)

其他几个提示:

  • 即使yytext是全球性的,也要假装它不是。您必须复制任何需要进一步处理的字符串。strdup是你的朋友;使用它,而不是摆弄CCD_ 17和CCD_ 18。同时使用asprintfchar* out; asprintf(&out, "%s%s%s", s1, s2, s3);是连接三个字符串的最简单的方法。对于没有这些东西的平台,有一些不受限制的实现很容易获得,所以不要担心"但它们不是Posix/StandardC/yadda-yadda-yahda"的论点。甚至不要考虑固定长度的缓冲区。你不需要它们。诚实
  • 另一方面,如果可以在扫描仪中处理令牌,请在那里进行处理。例如,数字;在扫描仪中执行一次strtol要容易得多,然后您甚至不需要考虑内存分配
  • 当您不再需要free()字符串时,不要忘记使用它们,但如果您发现很难从泄漏内存开始,然后在解析器工作后修复问题。(我知道有些人会觉得这是一种亵渎,但只要你记得在生产前这样做,那没关系;一旦你有了基本的工作,你就会觉得更有动力。)

最后:

  • 使用bison的合理最新版本。如果你发现自己有神秘的shift/reduce冲突,请使用glr解析器:是的,它有点慢,但如果它能为你节省一些痛苦,那就值得了。你可以随时回去解决问题。(GLR语法分析器不会让你免于所有语法问题。你仍然需要确保你的语法不含糊。但它们会有所帮助。)
  • 我个人建议:使用C接口。用C++编译是可以的,你可以使用标准的C++容器和其他不错的功能;只是不要在语义值中使用它们,因为这与bison的内部堆栈管理不兼容。(指向C++容器的指针也可以。)记住,flexbison只是控制流;你的大部分程序将用C/C++编写,所以你不会使用编译器工具进入一个新的世界。您也没有获得免费通行证:在开始编写解析器之前,您需要知道如何使用C/C++

希望能有所帮助。祝你好运

我会使用一个带有递归下降解析器的自制扫描仪,因为这是一个非常简单的解析任务,使用解析器生成器将花费与自己编写解析器一样多或更多的时间。

您可能会看到Boost Spirit,它允许您轻松编写词法分析器(Boost.Lex)和语法分析器(Boot.Qi)。它有一种有趣的方法,包括直接在C++代码中定义语法/语法,而不是使用单独的语法文件。它便携、标准、独立且非常优雅。

如果您的语言要发展成更复杂的东西,您可以考虑Flex和Bison。他们使用与Lex&Yacc,它们是旧的Unix等价物。这些工具的优点是有大量的垃圾。不方便的是,它们通过将骨架代码与语法文件中提供的片段混合来生成代码。因此,掌握和维护更为复杂。

但在你的特殊情况下,你有一种非常简单的语言,只有几个标记,显然是一个简单的"LL(1)语法"。(例如,解析器需要提前读取单个令牌,以无歧义地确定要解析什么)。编写自己的代码很容易,甚至可以使用<regex>来简化令牌扫描,并创建与您的语言结构相对应的对象。