开发者生态
morning
Show HN:Yantra – 用于 C++ 的 LALR(1) 解析器生成器
摘要
Yantra is a powerful compiler compiler and LALR(1) parser generator written in C++, with the following core features: An integrated lexer Built-in support for UNICODE/UTF8 input Built-in AST builder B...
hello
the
parser
cpp
and
from
with
for
input
build
2026-10-01
1 阅读
约8分钟阅读
renjipanicker
字号:
Yantra 是一个用 C++ 编写的强大的编译器编译器和 LALR(1) 解析器生成器,具有以下核心功能: 集成词法分析器 内置对 UNICODE/UTF8 输入的支持 内置 AST 构建器 内置 AST 遍历器 自下而上解析(即 LALR)和自上而下遍历(遍历) 多模式词法分析器,可用于实现嵌套多行注释等 词法分析器驱动(基于推送)解析器:读取输入一次一个字符,并在完成时将标记提供给解析器,这对于处理到达的输入(例如来自套接字)很有用。可选的合并模式,其中整个解析器生成为单个 cpp 文件,以及功能齐全的 main() 函数。或者,在非合并模式下,解析器生成为单独的 .hpp 和 .cpp 文件,准备放入现有项目中。 Yantra 这个名字在梵文中是机器的意思,就像本文中的状态机一样。 Yantra 除了 C++ 标准库之外没有任何依赖项,因此构建它是一个普通的 CMake 构建: git clone git@github.com:TantrixAuto/yantra.git cd yantra mkdir build && cd build cmake .. cmake --build 。这会在 bin/ 中生成 ycc 可执行文件。保存语法文件 hello.y : start := stmts; stmts := stmts stmt; stmts := stmt; stmt := ID; ID := "[A-Za-z]+"; WS := "\s"!;然后从中生成一个解析器: bin/ycc -c ascii -f hello.y -a 这会写入 hello.cpp (一个合并的、独立的解析器,有自己的 main() )和 hello.log 。使用任何 C++23 编译器对其进行编译: # clang clang++ --std=c++23 -o hello hello.cpp # gcc g++ --std=c++23 -o hello hello.cpp # MSVC (cl.exe, 来自开发人员命令提示符) cl /std:c++23 /EHsc /nologo hello.cpp 上面的语法可识别一个或多个以空格分隔的字母单词。 -s 将该字符串作为输入直接提供给解析器(与从文件读取的 -f 或从控制台交互读取的 -i 不同): # 静默成功 $ ./hello -s " hello world " $ echo $? 0 # -t1 打印解析后的 AST $ ./hello -s " hello world " -t1 0:start_1(1:stmts_1(2:stmts_2(3:stmt_1(4:ID(hello))) 2:stmt_1(3:ID(world))) 1:_tEND ()) # 失败:ID 只匹配字母,“123”不是此语法的有效输入 $ ./hello -s“你好123”s1-err:? a1.in(001,007):TOKEN_ERROR{{token: }} 你好 123 $ echo $? 1 Yantra 首先将整个输入解析为 AST,然后自上而下调用语义操作,这与大多数解析器生成器不同,在大多数解析器生成器中,操作随着每个规则的减少而自下而上运行。这种顺序使得父规则的操作可以在其子规则被访问之前运行。将其另存为 calc.y : %class Calculator;开始 := 表达式; expr := expr(a) PLUS expr(b) %{ std::cout << "添加" << std::endl; %} expr := NUMBER(N) %{ std::cout << "数字:" << N.text << std::endl; %} 数字 := "\d+";加号 := "\+"; WS := "\s+"!;按照与上面相同的方式生成并编译它( bin/ycc -c ascii -f calc.y -a ,然后是三个编译器命令中的任何一个),然后运行它: $ ./calc -s " 1 + 2 + 3 " 添加数字:1 添加数字:2 数字:3 1 + 2 + 3 左关联解析为 (1 + 2) + 3 。因此,外部的 Adding (树的根)首先打印,然后是其左子节点(Number: 1),然后是其右子节点,右子节点本身就是另一个具有自己的两个子节点的 Adding 节点。手写的递归下降或自下而上的解析器必须构建额外的 AST 类和单独的遍历过程才能获得此排序。这里它直接脱离了语法。请参阅下面的构建说明和教程,了解语法语法的真正演练。与 Bison / Yacc / Lemon(经典的 LALR(1) 系列)对比:来自 SQLite 的 Lemon 是 Yantra 的直接灵感来源。当每个规则自下而上减少时,它们在解析期间运行语义操作。 Yantra 始终首先构建完整的 AST,然后在单独的传递中自上而下地遍历它,因此父规则的操作可以在访问其子规则之前运行。单个语法还可以定义多个 walker(例如,一个从同一解析发出 C++,另一个发出 Java)。将其中任何一个从 Bison 家族中取出都意味着手工构建您自己的 AST 并在其上构建 Walker。与 ANTLR 相比:ANTLR 也遍历完全构建的解析树,但这来自其 LL(*) 算法,该算法在解析时已经自上而下地构建了树。 Yantra 从 LALR(1) 中获得了相同的自上而下的遍历,这是一种自下而上的算法,在解析过程中没有自然的“整棵树存在”时刻,同时保持 LALR(1) 相对于自适应 LL(*) 的时间和空间效率。除此之外,Yantra 仅针对 C++(ANTLR 生成多种语言),并提供自己的带有模式堆栈支持的集成词法分析器,而不是单独的词法分析器生成器。 ANTLR 自己的生成器工具是 Java,因此从 C++ 项目中使用它意味着将 JVM 添加到构建工具链只是为了运行生成器。 Yantra 是一个本机 C++ 可执行文件,没有此类依赖性。 ANTLR 更为成熟且应用广泛。 Yantra 是一个更小、更新、单一维护的项目。与树上看护者:完全不同的问题。它是为增量、容错而构建的
这篇文章对您有帮助吗?
订阅66必读
每日精选科技资讯,直达你的邮箱