Perl 6 语法,第1部分
Perl 6 语言内置了对 语法 的支持。您可以将语法视为正则表达式和诸如 yacc
或 bison
等实用工具的结合,或者更复杂的语法工具,如 ANTLR。所有这些——包括词法分析器、解析器和语义处理——通常是编译器的独立部分,都是内置的,并且可以在安装了新的 Perl 6 之后立即使用。
要感受到语法的强大之处,只需说一点:Perl 6 的自身语法是用 Perl 6 编写的,作为一个巨大的语法类 Perl6::Grammar。
在这篇文章中,我将通过一些示例来阐述语法的基础知识。所有必需的语言结构将在进行过程中解释。
解析数字
解析数字似乎是一个简单的任务,直到你开始考虑用户可能使用的不同格式,包括负数、浮点数、科学记数法中的数字以及 C 的长长整数等特殊形式的数字。
让我们从最简单的形式开始:数字作为一串数字的序列。例如,1、42、123 或 1000。Perl 6 的语法是一种特殊的类,有自己的关键词。语法的第一个规则必须(默认)命名为 TOP
,以下是完全解析我们第一组数字的程序
grammar N {
token TOP {
<digit>+
}
}
for <1 42 123 1000 -3> -> $n {
say N.parse($n) ?? "OK $n" !! "NOT OK $n";
}
当调用 N
语法的 parse
方法时,Perl 会尝试将给定的字符串与 TOP
方法进行匹配。在我们的例子中,这是一个 token
,这意味着字符串的各个部分之间不能有任何可选的空格。由于 TOP
仅在消耗整个字符串时才成功,因此不需要使用显式的锚点 ^
和 $
来绑定标记的边缘。
与正则表达式一样,标记和规则可以包括其他标记、规则或通过它们的名称引用的正则表达式。在我们的第一个例子中,TOP
标记需要匹配数字的内置方法 digit
。与标准 Perl 5 正则表达式中的量化符相同,+
量化符允许前面的原子重复一次或多次。
到目前为止,我们的简单语法只能解析无符号整数。任何负数都无法解析
OK 1
OK 42
OK 123
OK 1000
NOT OK -3
让我们更新语法并引入表示可选符号的标记,该符号可以是 +
或 -
grammar N {
token TOP {
['+' | '-']?
<digit>+
}
}
在这里,方括号将两个备选方案组合在一起:'+' | '-'
。量化符 ?
要求只有一个这样的字符,或者没有。在 Perl 6 中,方括号仅创建一个分组,但不捕获其内容。请注意,+
和 -
都被引用,因为 Perl 6 将任何非字母数字字符视为特殊字符,除非它被引用或用 \
转义。
下一步是添加对浮点数的支持。一个临时的解决方案可以创建一个包含数字和 '.'
字符的字符类,但这将是完全错误的。例如,包含两个点的字符串 3..14
会通过这个过滤器。所以,不要这样做
grammar N {
token TOP {
['+' | '-']?
<digit>+
['.' <digit>+]?
}
}
这个语法现在允许一个可选部分,该部分由点和另一串数字组成,当数字是整数或包含显式的小数部分时,它工作得很好,例如 3.14
。它对于缺少其中一个部分的数字失败:3.
或 .14
。
尝试使用量词使部分内容可选会使语法难以阅读且容易出错。例如,以下标记符匹配上述所有数字,但也匹配单个 .
grammar N {
token TOP {
['+' | '-']?
<digit>*
['.' <digit>*]?
}
}
是时候引入更多标记符了。将数字序列提取为单独的标记符,并明确列出所有变体
grammar N {
token TOP {
<sign>?
<value>
}
token sign {
'+' | '-'
}
token digits {
<digit>+
}
token value {
| <digits> '.' <digits>
| '.' <digits>
| <digits> '.'
| <digits>
}
}
value
标记符封装了变体:它包含接受数字的四种不同表示形式。竖线将它们分隔开。为了统一,允许在第一个变体之前添加一个额外的竖线,这样所有变体都可以用简单的ASCII艺术强调。
当前的语法已经足够智能,可以拒绝单个点号
OK 1
OK 42
OK 123
OK 1000
OK -3
OK 3.14
OK 3.
OK .14
NOT OK .
最后一步是支持科学记数法中的数字。为此添加另一个变体是一个简单的候选方案
grammar N {
token TOP {
<sign>?
[
| <value> <exp> <sign>? <digits>
| <value>
]
}
token sign {
'+' | '-'
}
token exp {
'e' | 'E'
}
token digits {
<digit>+
}
token value {
| <digits> '.' <digits>
| '.' <digits>
| <digits> '.'
| <digits>
}
}
用以下案例测试语法
for <1 42 123 1000 -3
3.14 3. .14 .
-3.14 -3. -.14
10E2 10e2 -10e2 -1.2e3 10e-3 -10e-3 -10.2e-33
> -> $n {
say N.parse($n) ?? "OK $n" !! "NOT OK $n";
}
一切正常。但是等等,在Perl中,下划线也被允许出现在数字中!拥有合适的语法,添加对此的支持很容易;只需修改 digits
标记符即可
token digits {
<digit>+ ['_' <digit>+]?
}
不符合规则的字符串仍然被忽略
OK 100_000
NOT OK _1
NOT OK 1_
NOT OK 1__0
结论
通过几个简单的步骤,我们创建了一个可以理解不同格式数字的语法。作为练习,你可以添加对前缀 0x
、0b
、0o
(十六进制、二进制和八进制)以及后缀(如C中的 1000L
)的支持。语法仅用于检查数字格式的有效性,其功能远不止于此。在Perl 6中,你可以在语法中添加 动作;这些是当相应的规则或标记符成功匹配时执行的代码块。但这是另一天的故事。
标签
Andrew Shitov
Andrew自2000年起一直是Perl 6和Raku的爱好者,是多本书籍的作者,也是Perl和Raku活动的组织者,包括三个年度欧洲会议。目前正在撰写他的新书《使用Raku创建编译器》。
浏览他们的文章
反馈
这篇文章有什么问题吗?请通过在GitHub上打开问题或拉取请求来帮助我们
- More commenting... maybe?
github.polettix.it - Perl Weekly Challenge 121: Invert Bit
blogs.perl.org - Web nostalgia: MojoX::Mechanize
github.polettix.it - On the eve of CPAN Testers
blogs.perl.org - PWC121 - The Travelling Salesman
github.polettix.it - PWC121 - Invert Bit
github.polettix.it - Floyd-Warshall algorithm implementations
github.polettix.it - Perl Weekly Challenge 120: Swap Odd/Even Bits and Clock Angle
blogs.perl.org - How I Uploaded a CPAN Module
blogs.perl.org - App::Easer released on CPAN
github.polettix.it