Perl 6 Grammars, Part 1
Perl 6 语言内置了对 grammar 的支持。您可以将 grammars 视为众所周知的正则表达式和诸如 或 等实用程序或更复杂的 grammar 工具(如ANTLR)的组合。所有这些 - 词法分析器,语法分析器和语义处理 - 通常是编译器的独立部分,在 Perl 6 中它们都是内置的,并且可以通过全新的 Perl 6 安装 进行开箱即用。
要感受 grammar 的力量,Perl 6 自己的 grammar 就是用 Perl 6 庞大的 grammar 类 Perl6::Grammar写成的就足以说明了。
在本文中,我将通过几个例子来说明 grammar 的基础知识。所有必需的语言结构将在我们进行的时候进行解释。
解析数字
在你开始思考用户可以使用不同格式的数字,包括负数,浮点数,科学记数法中的数字,特殊形式的数字(如C的长整数)之前,解析数字似乎是一项简单的任务。
让我们从最简单的形式开始:一个数字作为数字序列。例如,1,42,123 或 1000. Perl 6 中的 grammar 是一种特殊的类,它有自己的关键字。grammar 的第一个 rule 必须(默认情况下)称为 ,以下是解析第一组数字的完整程序:
grammarN{
tokenTOP{
}
}
for ->$n{
sayN.parse($n) ??"OK $n"!!"NOT OK $n";
}
当调用 grammar 的 方法时,Perl 会尝试将给定的字符串与 方法进行匹配。在我们的例子中,这是一个 ,这意味着字符串不能在 token 的各个部分之间包含任何可选空格。 只在消耗整个字符串时才成功,因此不需要使用显式锚点 和 来绑定 token 的边缘。
与正则表达式一样,token 和 rule 可以包含其他 tokens,rules 或由其名称引用的正则表达式。在我们的第一个例子中, token 需要与数字匹配的 内置方法。 量词与标准的 Perl 5 正则表达式中的量词相同:它允许前一个原子重复一次或多次。
我们简单的 grammar 到目前为止只能解析无符号整数。任何负数都不能被解析:
OK 1
OK 42
OK 123
OK 1000
NOT OK -3
让我们更新 grammar 并引入可选符号的 token,它可以是 或 :
grammarN{
tokenTOP{
['+''-']?
}
}
在这里,方括号将两个选项组合在一起:。 量词要求只有一个这样的字符,或者没有。在 Perl 6 中,方括号只创建一个分组,但不捕获它的内容。还要注意, 和 都被引号引住了,因为 Perl 6 将任何非字母数字字符视为特殊字符,除非它被引号引起来或被转义。
下一步是添加对浮点的支持。临时解决方案可以创建包含数字和 符号的字符类。但这是完全错误的,例如,带有两个点的字符串(如 )通过此过滤器。所以,做点不一样的事情:
grammarN{
tokenTOP{
['+''-']?
['.' +]?
}
}
该 grammar 现在允许包含一个可选的点和另一个数字序列,并且在数字是整数或包含明确的小数部分(例如 )时可以很好地工作。对于其中一个部分丢失的那些数字,则失败: 或 。
通过使用量词使得零件可选的尝试使得 grammar 变得难以阅读并且容易出错。例如,以下 token 匹配上述所有数字,还有单个 :
grammarN{
tokenTOP{
['+''-']?
['.' *]?
}
}
现在是时候引入更多的 token 了。将数字序列分解为单独的 token 并明确列出所有变体:
grammarN{
tokenTOP{
}
tokensign{
'+''-'
}
tokendigits{
}
tokenvalue{
'.'
'.'
'.'
}
}
token 封装了这些变体:它包含可接受数字的四种替代表示。垂直条 分割它们。为了统一起见,允许在第一个备选分支之前添加一个附加的竖线 ,以便所有这些都通过简单的 ASCII 艺术强调。
当前的 grammar 已经足够聪明以拒绝单个点号:
OK 1
OK 42
OK 123
OK 1000
OK -3
OK 3.14
OK 3.
OK .14
NOT OK .
最后一步是以科学记数法支持数字。增加另一个备选分支易如反掌:
grammarN{
tokenTOP{
[
?
]
}
tokensign{
'+''-'
}
tokenexp{
'e''E'
}
tokendigits{
}
tokenvalue{
'.'
'.'
'.'
}
}
用以下例子测试我们的 grammar:
for
3.14 3. .14 .
-3.14 -3. -.14
10E210e2-10e2-1.2e310e-3 -10e-3 -10.2e-33
sayN.parse($n) ??"OK $n"!!"NOT OK $n";
}
一切正常。但在 Perl 中,数字中也允许有下划线! 有一个合适的语法,增加对此的支持是容易的; 应只修改 token:
tokendigits{
+ ['_' +]?
}
不遵循该 rule 的字符串仍然会被忽略:
OK 100_000
NOT OK _1
NOT OK 1_
NOT OK 1__0
结论
只需几个简单的步骤,我们就可以创建一个能够理解不同格式数字的 grammar。作为练习,您可以添加前缀 ,,(十六进制,二进制和八进制)和后缀(如C中的 )的支持。Grammars 只被用来检查数字格式的有效性,并且它们的能力并没有在那里结束。在 Perl 6 中,您可以将actions添加到 grammar 中; 这些是在相应的 rule 或 token 已成功匹配时执行的代码块。但那是另一天的故事。
领取专属 10元无门槛券
私享最新 技术干货