第 12 章 语言扩展

13 扩展节点

(在 OCaml 4.02 中引入,为除表达式以外的构造添加中缀符号是在 4.03 中,中缀符号 (e1 ;%ext e2) 在 4.04 中添加。)

扩展节点是语法树中的通用占位符。它们被类型检查器拒绝,并旨在被外部工具(例如 -ppx 重写器)“扩展”。

扩展节点与属性 ‍12.12具有相同的标识符和有效负载概念。

第一种扩展节点形式用于“代数”类别

extension::= [%attr-idattr-payload]
 
expr::= ...
 extension
 
typexpr::= ...
 extension
 
pattern::= ...
 extension
 
module-expr::= ...
 extension
 
module-type::= ...
 extension
 
class-expr::= ...
 extension
 
class-type::= ...
 extension
 

第二种扩展节点形式可以在结构和签名中使用,无论是在模块还是对象语言中

item-extension::= [%%attr-idattr-payload]
 
definition::= ...
 item-extension
 
specification::= ...
 item-extension
 
class-field-spec::= ...
 item-extension
 
class-field::= ...
 item-extension
 

当有效负载是同类(表达式与表达式、模式与模式...)时,可以使用扩展节点的中缀形式。

示例

let%foo x = 2 in x + 1     === [%foo let x = 2 in x + 1]
begin%foo ... end          === [%foo begin ... end]
x ;%foo 2                  === [%foo x; 2]
module%foo M = ..          === [%%foo module M = ... ]
val%foo x : t              === [%%foo: val x : t]

当这种形式与属性的中缀语法一起使用时,属性被认为适用于有效负载

fun%foo[@bar] x -> x + 1 === [%foo (fun x -> x + 1)[@bar ] ];

为了方便起见,可以使用额外的简写形式 let%foo x in ...,用于实现绑定运算符(参见 12.23.4)。

此外,引号字符串 {|...|} 可以与扩展节点结合使用,以嵌入外来语法片段。这些片段可以被预处理器解释并转换为 OCaml 代码,而无需转义引号。它们可以使用语法快捷方式。

{%%foo|...|}               === [%%foo{|...|}]
let x = {%foo|...|}        === let x = [%foo{|...|}]
let y = {%foo bar|...|bar} === let y = [%foo{bar|...|bar}]

例如,您可以使用 {%sql|...|} 来表示任意的 SQL 语句 - 假设您有一个识别 %sql 扩展的 ppx-rewriter。

请注意,单词分隔形式,例如 {sql|...|sql},不应用于表示正在使用扩展。实际上,用户无法从代码中看出此字符串文字是否具有与他们期望不同的语义。此外,对特定分隔符赋予语义会限制更改分隔符的自由度,以避免转义问题。

13.1 内置扩展节点

(在 OCaml 4.03 中引入)

一些扩展节点由编译器本身理解

type t = .. type t += X of int | Y of string let x = [%extension_constructor X] let y = [%extension_constructor Y]
# x <> y;;
- : bool = true