75 个重大版本更新
新增 0.97.1 | 2024 年 8 月 21 日 |
---|---|
0.96.1 | 2024 年 7 月 29 日 |
0.91.0 | 2024 年 3 月 5 日 |
0.88.1 | 2023 年 12 月 14 日 |
0.7.0 | 2019 年 12 月 17 日 |
#316 在 解析器实现 中排名
5,421 每月下载量
在 28 个 包中使用(直接使用 18 个)
2MB
45K SLoC
nu-parser,Nushell 解析器
Nushell 的解析器是一种类型驱动解析器,这意味着解析器将使用在解析时可用的事务信息来配置解析器。这允许它处理更广泛的技巧来处理命令的参数。
Nushell 的基本语言是以空白字符分隔的令牌,命令(Nushell 对函数的称呼)名称位于头部位置
head1 arg1 arg2 | head2
词法分析
解析器的第一个任务是进行词法分析,以找到输入中令牌的开始和结束位置。这会将上面的内容转换为以下内容
<item: "head1">, <item: "arg1">, <item: "arg2">, <pipe>, <item: "head2">
此时,解析器对命令的形状或如何解析其参数几乎没有理解。
轻量级解析
由于 Nushell 是一种管道语言,管道在分隔命令以及表示命令之间信息流方面发挥着关键作用。轻量级解析阶段,正如其名称所示,有助于将词法化令牌分组。
在上面的轻量级解析阶段,以下转换发生
Pipeline:
Command #1:
<item: "head1">, <item: "arg1">, <item: "arg2">
Command #2:
<item: "head2">
解析
当解析进入解析阶段时,真正的魔法开始发生。此时,它遍历轻量级解析树,并对每个命令做出决定
- 如果命令看起来像一个内部/外部命令字面量:例如
foo
或/usr/bin/ls
,则将其解析为内部或外部命令 - 否则,将命令解析为数学表达式的一部分
类型/形状
每个命令都会为其读取的每个参数分配一个形状。这些形状有助于定义解析器如何处理解析。
例如,如果命令编写如下:
where $x > 10
在解析发生时,解析器将查找 where
命令并找到其签名。签名声明了允许哪些标志以及允许哪些位置参数(既包括必需的也包括可选的)。每个参数都附带一个形状,定义了如何解析值以获取该位置。
在上面的示例中,如果 where
的签名表明它接受三个字符串值,则结果将是:
CallInfo:
Name: `where`
Args:
Expression($x), a String
Expression(>), a String
Expression(10), a String
或者,签名可以声明它接受三个位置参数:一个变量、一个运算符和一个数字,这将给出:
CallInfo:
Name: `where`
Args:
Expression($x), a Variable
Expression(>), an Operator
Expression(10), a Number
请注意,在这种情况下,每个参数都会在编译时进行检查,以确认表达式具有所需的形状。例如,"foo"
将无法解析为数字。
最后,某些形状可以消耗多个标记。在上面的例子中,如果 where
命令声明它接受一个必需的单个参数,并且该参数的形状是数学表达式,那么解析器将把剩余的标记视为数学表达式的一部分。
CallInfo:
Name: `where`
Args:
MathExpression:
Op: >
LHS: Expression($x)
RHS: Expression(10)
当命令运行时,它现在可以将其作为一个单独的步骤来评估整个数学表达式,而不是进行任何额外的解析来了解参数之间的关系。
留出空间
由于某些形状可以消耗多个标记,因此解析器允许尽可能和平地共存多个形状是很重要的。
它执行此操作的最简单方法是确保为每个必需参数至少有一个标记。如果命令的签名表明它接受一个数学表达式和一个数字作为两个必需的参数,那么解析器将在数学解析器停止之前停止一个标记。这允许第二个形状消耗最后一个标记。
解析器留出空间的另一种方法是查找签名中的关键字形状。关键字是对于此命令来说是特殊的词。例如,在 if
命令中,else
是一个关键字。当它在参数中找到时,解析器将其用作标志,指示为每个形状留出空间的位置。然后,在 else
之前的标记将被输入到 else
之前的签名部分,而随后的标记将被 else
和随后的形状消耗。
依赖关系
~22–56MB
~1M SLoC