1 Parsing and Specifying Syntax
1.5 Syntax Patterns
syntax/parse支持四种语句模式:
- 单目模式,简称S-pattern
- 头部模式,简称H-pattern
- 三点头部模式,简称EH-pattern
- 动作模式,简称A-pattern
1.5.1 Single-term Patterns
S-pattern = pvar-id
| pvar-id:syntax-class-id
| pvar-id:literal-id
| literal-id
| (~vars- id)
| (~vars+ id syntax-class-id maybe-role)
| (~vars+ id (syntax-class-id arg ...) maybe-role)
| (~literal literal-id maybe-phase)
| atomic-datum
| (~datum datum)
| (H-pattern . S-pattern)
| (A-pattern . S-pattern)
| (EH-pattern ... . S-pattern)
| (H-pattern ...+ . S-pattern)
| (~ands proper-S/A-pattern ...+)
| (~or*s S-pattern ...+)
| (~not S-pattern)
| #(pattern-part ...)
| #s(prefab-struct-key pattern-part ...)
| #&S-pattern
| (~rest S-pattern)
| (~describes maybe-opaque maybe-role expr S-pattern)
| (~commits S-pattern)
| (~delimit-cuts S-pattern)
| (~posts S-pattern)
| A-pattern
用来描述单个条目,有点类似与传统的syntax-rules以及syntax-case,但是有更强的表述能力。
单目并不意味着原子,单目模式可有有很复杂的结构。
一个本分的单目模式不能是一个动作模式
连对模式其实可以看成拥有特定结构的单目表达式,用以匹配本分的连对。
单目表达式的化身:
- id,可以是模式变量,类注模式变量,或者一个字面量
- 对于字面量,需要多解释以下。如果id是字面量列表中出现的,那么其等同于
(~literal id)
- 如果id的构成形式是
pvar-id:syntax-class-id
,那么是类注模式变量,等同于(~var pvar-id syntax-class-id)
。pvar-id部分可省,光:syntax-class-id
也是合法的。也可以用#:declare
来声明syntax-class-id。 - 如果id的构成是
pvar-id:literal-id
,且literal-id在字面量列表中有声明,那么其等同于(~and (~var pvar-id) literal-id)
- 若非上述形式,则id是一个普通的模式变量,等同于
(~var id)
。
- 对于字面量,需要多解释以下。如果id是字面量列表中出现的,那么其等同于
(~var pvar-id)
,一个模式变量。如果pvar-id没有语句类注(通过#:convention指定),那么可以匹配任何标的。pvar-id会对照到所匹配的条目。一个例外,如果是_
,则不会进行对照。- 如果pvar-id带有语句类注,则等同于类注模式变量。
(~var pvar-id syntax-class-use maybe-role)
,根据类注来匹配模式变量。类注会提供嵌套的属性,以pvar-id后跟点号这种形式来提供。- 如果pvar-id是
_
,没有属性会被对照到。如果pvar-id是||
,则pvar-id本身不会被对照,但是嵌套的属性会以无前缀的形式提供。 - 如果提供了role-expr,则会被用在错误信息中。
- 如果pvar-id是
(~literal literal-id maybe-phase)
,匹配能够和literal-id
在free-identifier=?
调用中相等的标识符。可以通过maybe-phase
来指定目标的位阶。literal-id的对照必须存在,如果不存在,可以自行定义一个默认抛出异常的同名化用器。atomic-datum
,以字面量形式匹配数量值,字符串,布尔值,关键字以及空连对。(~datum datum)
,以equal?
来匹配能和datum相等的标的。会使用syntax->datum将目标转为数据。- 此形式可以按符号的形式来识别标识符,类似的~literal则按对照来识别标识符。
(H-pattern . S-pattern)
,复合模式,可以将一个单目模式拆分成一个头部模式和另一个单目模式。- 此模式甚至可以匹配非恰当连对;如果头部模式可以匹配零长度头部,那么整个模式则等同于其内嵌的单目模式
- 头部模式可以是一个单目模式,这样一来就成了两个单目模式的子对了。
(A-pattern . S-pattern)
先执行前项的动作模式,再匹配后项中的单目模式。- 由于动作模式只是执行动作,不参与匹配,所以可以看成动作模式不占空间。常见的作法是把动作模式插入到连对模式中,例如:
(x y ~! z)
。
- 由于动作模式只是执行动作,不参与匹配,所以可以看成动作模式不占空间。常见的作法是把动作模式插入到连对模式中,例如:
(EH-pattern ... . S-pattern)
,匹配的目标是一个连对,其头部可以是同一EH-pattern的重复匹配。- EH-pattern的特点是将众多候选项放在一起形成一个群组。
(H-pattern ...+ . S-pattern)
,和(H-pattern . S-pattern)
类似,但是要求必须要要有一个头部模式。(~and S/A-pattern ...)
,目标必须能分别匹配所有指定的子模式。子模式可以混合单目模式和动作模式,但必须至少有一个单目模式。一个子模式对照的属性可以供其后的子模式使用。- 作用跟语句类注有点类似,但是更轻量一些。
(~or* S-pattern ...)
,能匹配指定的子模式之一即可,尝试顺序为从左向右。子模式列出的所有模式变量都会被对照到,即便没有匹配到(会对照到#f)。(~not S-pattern)
,匹配任意条目,只要该条目不与指定的模式匹配。子模式的属性的在此模式之外无法访问。- 跟~and配合使用的例子:
(~and before (~not =>)) ...
可以匹配x y z =>
中的x,y和z。
- 跟~and配合使用的例子:
#(pattern-part ...)
,匹配项列#s(prefab-struct-key pattern-part ...)
,匹配预置结构#&S-pattern
,匹配项封(~rest S-pattern)
,如同S-pattern
,不过用在读取器不允许不适连对的时候,比如项列或者结构模式(~describe maybe-role maybe-opaque expr S-pattern)
,用来在错误报告中添加额外的信息(~commit S-pattern)
,用来影响回溯过程:- 如果模式成功匹配,那么此模式的子模式中创建的所有决策点将会被丢弃,如果此模式之后的模式匹配失败,那么只会回溯到此模式之前的决策点
~!
在此模式中只会清除此模式内的决策点,如果~delimit-cut
。
(~delimit-cut S-pattern)
,用来影响回溯过程:- 此模式内的
~!
只会清除此模式内创建的决策点
- 此模式内的
(~post S-pattern)
,如果子模式中出错,则跳到下一步报告错误信息。用于化解匹配优先级问题。A-pattern
,在没有歧义的情况下,一个动作模式可以当作一个单目模式,可匹配任意标的
1.5.2 Head Patterns
H-pattern = pvar-id:splicing-syntax-class-id
| (~varh id splicing-syntax-class-id maybe-role)
|
(~varh id (splicing-syntax-class-id arg ...)
maybe-role)
| (~seq . L-pattern)
| (~andh proper-H/A-pattern ...+)
| (~or*h H-pattern ...+)
| (~optionalh H-pattern maybe-optional-option)
| (~describeh maybe-opaque maybe-role expr H-pattern)
| (~commith H-pattern)
| (~delimit-cuth H-pattern)
| (~posth H-patter)
| (~peek H-pattern)
| (~peek-not H-pattern)
| proper-S-pattern
头部模式用于匹配在连对中出现的,不处于尾部的标的。其特长是可以匹配任意数目的子对。可以处理可选的构造,比如使用关键字指定的参量。
一个本分的头部模式不能是一个单目模式
pvar-id:splicing-syntax-class-id
,等同于(~var pvar-id splicing-syntax-class-id)
- 使用拼接语句类注来给pvar-id做类注
(~seq . L-pattern)
,匹配一个条目序列,这个序列如果作为连对,必须能够被L-pattern匹配- 例子:
((~seq 1 2 3) 4)
匹配#'(1 2 3 4)
- 例子:
(~and H-pattern ...)
, 如果单目模式中的~and,但是匹配一个条目序列- 此处的H-pattern必须是本分的。这是有意而为之的设计,主要是为了防止误用。
(~or* H-pattern ...)
,如果单目模式中的~or*,但是匹配一个条目序列(~optional H-pattern maybe-optional-option)
,可选匹配,可以通过#:defaults来指定默认值。(~describe expr H-pattern)
,如果单目模式中的~describe,但是匹配一个头部模式(~commit H-pattern)
,如同单目模式中的~commit,但是匹配一个头部模式(~delimit-cut H-pattern)
,如同单目模式中的~delimit-cut,但是匹配一个头部模式(~post H-pattern)
,如同单目模式中的~post,但是匹配一个头部模式(~peek H-pattern)
,匹配一个头部模式,却不占有其条目(~peek-not H-pattern)
,成功匹配意味着指定的H-pattern匹配失败。同样不占用被匹配中的条目S-pattern
,匹配一个单目模式
1.5.3 Ellipsis-head Patterns
一个三点头部模式可以描述任意数量的条目,如同普通的头部模式,但是有额外的约束用以限制条目的重复次数。常用的例子是匹配以任意次序出现的关键字合集。合集是用~alt
来指定。
(~alt EH-pattern ...)
,如果指定的任意子模式匹配,此模式就匹配成功(~once H-pattern once-option ...)
,确保指定的H-pattern匹配成功有且仅有一次。once-option...
,用于匹配太多或者太少的时候的错误描述。(~optional H-pattern optional-option ...)
,确保指定的H-pattern匹配成功最多成功一次。once-option...
,用于匹配太多的时候的错误描述。可以通过#:defaults
选项来指定默认值。(~between H-pattern min-number max-number between-option ...)
,确保指定的H-pattern匹配成功的次数处于某个范围。once-option...
,用于匹配太多或者太少的时候的错误描述。
1.5.4 Action Patterns
一个动作模式并不描述任何语句;但是可以对匹配过程施加影响。
~!
,消除操作符,消除原路折回用的决策点,提交当前分支的模式。~!
以外围最近的~delimit-cut
或者~commit
为参照点。如果没有外围的~describe
但是消除发生在一个语句类注的定义中,那么类注之中的决策点会被丢弃。~!
不允许出现在~not
模式中,除非有相应的~delimit-cut
或者~commit
模式
(bind [attr-arity-decl expr] ...)
,将表达式的结果对照到attr-ids。(~fail maybe-fail-condition maybe-message-expr)
,如果没有指定失败条件,或者指定的失败条件成立,那么模式以给定的异常信息抛出错误。(~parse S-pattern stx-expr)
,将stx-expr的结果转化为一个语句标的,并将其与S-pattern进行匹配。(~and A-pattern ...+)
,执行所指定的每一个A-pattern。(~do defn-or-expr ...)
,给定一个混杂着定义式或者表达式的序列,逐个运算之。定义式中的对照可供后续表达式使用。在此模式之前的匹配中产生的属性可访问。- 此模式中不可生成新的属性对照,尝试的话会出错
(~undo defn-or-expr ...)
,正常匹配的时候无效果,但是在原路折回的过程中如果经过则会触发。- 和
~do
配对可以控制侧面效果的产生和反解
- 和
(~post A-pattern)
,如果单目模式中的~post
,但是指定的是动作模式。
1.5.5 Pattern Expanders
通过模式扩展器可以提供额外的模式定义。通常新的模式会被转化成既有的模式。
(pattern-expander proc) → pattern-expander?
定义一个模式转化器prop:pattern-expander
,将结构辖属作为一个模式转化器(pattern-expander? v) → boolean?
,判定是否是一个模式转化器(syntax-local-syntax-parse-pattern-introduce stx) → syntax?
,为向后兼容而存在,等同于syntax-local-introduce
。
(本篇完)