The Racket Reference阅读笔记。
Macros
12.1 Pattern-Based Syntax Matching
syntax-case语法如下:
(syntax-case stx-expr (literal-id ...)
clause ...)
其中clause用来指定模式:
clause = [pattern result-expr]
| [pattern fender-expr result-expr]
pattern则是:
pattern = np-pattern
| (pattern ...)
| (pattern ...+ . np-pattern)
| (pattern ... pattern ellipsis pattern ... . np-pattern)
首先pattern可以分成括起来的模式,以及非括起来的模式(np-pattern)。
(pattern ...)
表示可以有在括号内嵌套列举任意个模式,(pattern ...+ . np-pattern)
类似,但是用于在某个非括起来的模式之前列举一至多个模式,也就是说不管制定了多少个模式,必须以np-pattern
收尾。
对于(pattern ... pattern ellipsis pattern ...)
则允许在模式中使用字面上的ellipsis,也就是...
,例如a ...
。在这个跟着三点的模式之前可以列举任意多个模式,在其之后也可以列举任意多个模式。当然,也可以指定一个np-pattern作为收尾模式。
np-pattern则可以表示为:
np-pattern = _
| id
| #(pattern ...)
| #(pattern ... pattern ellipsis pattern ...)
| #&pattern
| #s(key-datum pattern ...)
| #s(key-datum pattern ... pattern ellipsis pattern ...)
| (ellipsis stat-pattern)
| const
- #号开头的是列项,封箱还有预制结构。
- _ 匹配任意对象但是不生成绑定
- id匹配…或者_或者字面值以外的标识符,并且会被绑定为模式变量。模式变量是一个转换器绑定,只能在
syntax
中使用,其值是一个深度标记为零的语法对象。 - id为字面值id的情况下,跟目标字面值id具有free-identifie=?相等性,匹配的结果不生成模式变量。
- const在np-pattern中用于收尾,如果前面的项目都不匹配,那么就会试着当做常量值来匹配(equal?同一性),也就是说,如果指定
1
,就会呗当做'1
来目标值匹配。
(ellipsis stat-pattern)
需要额外说明一下,它表现为:
stat-pattern = id
| (stat-pattern ...)
| (stat-pattern ...+ . stat-pattern)
| #(stat-pattern ...)
| const
其实是用于将模板中指定的...
当做正常的...
,而不是将其视为重复操作。
syntax-case*
和syntax-case
类似,但是在clause之前接受一个id-compare-expr,其接受两个参量,一是字面值id,二是模板中的标识符。id-compare-expr要给出二者是否具有free-identifier=?同一性。
with-syntax
,有点类似于:
(syntax-case (list stx-expr ...) ()
[(pattern ...) (let () body ...+)])
with-syntax会对列举的每个模式进行匹配,如果模式对象的表达式返回的不是语法对象,则会通过datum->syntax
将其转化为表达式。
上面列举的模式用于匹配和拆分语法对象,下面提到的syntax语法模板则用来重构语法对象。
(syntax template)
template = id
| (head-template ...)
| (head-template ...+ . template)
| #(head-template ...)
| #&template
| #s(key-datum head-template ...)
| (~? template template)
| (ellipsis stat-template)
| const
head-template = template
| head-template ellipsis ...+
| (~@ . template)
| (~? head-template head-template)
| (~? head-template)
stat-template = like template, but without ..., ~?, and ~@
ellipsis = ...
syntax模板不仅能够将模式变量替换成对应的值,还能做一些额外的控制。
其 (ellipsis stat-template)
用来关闭...
,~?
以及~@
的特殊含义。
其head-template
用于受括列表中的前置项(因为列表的尾项不可是head-template,否则递归无法终止。注意,head-template
包含了template。也就是递归的前置项可以有更多花样。
(~? template1 template2)
中,如果template1有缺失变量则应用template2,有点像C++中的SFINAE,没有有?
(~? head-template)
可以生成空项。
(~@ . template)
用于展开和拼接template生成的连对。
head-template ellipsis ...+
有点费解,可以想象成在head-template所包含的模式变量上进行map操作。迭代的次数取决于模板中模式变量的值。如果将outer当做三点尾随的inner。如果和outer的深度标记相同,那么模式变量就是outer的迭代模式变量。至少要有一个这种类型的变量,否则就出错了。如果有多个此种类型的变量,那么必须有相同个数的元素。outer的结果是将inner模板应用到迭代模式变量而获得的,每次应用的时候将深度标记减一。ellipsis可以指定一至多个,没指定一次则深入迭代一次,但是不能超过迭代模式变量的深度。
(syntax-case #'((1 2) (3 4)) ()
[((l ...) ...) #'(l ... ...)])
.#<syntax:interactions from an unsaved editor:22:21 (1 2 3 4)>
其他相关函数
(quasisyntax template)
(unsyntax expr)
(syntax/loc stx-expr template)
,额外指定文法信息(quasisyntax/loc stx-expr template)
(quote-syntax/prune id)
,剔除id的某些作用域(syntax-pattern-variable? v) → boolean?
判断是否是模式变量,在位阶0好像不能用
(本篇完)