Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

06 Jun 2021

Racket的Syntax/Parse学习笔记【一】

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)
  • (~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,则会被用在错误信息中。
  • (~literal literal-id maybe-phase),匹配能够和literal-idfree-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。
  • #(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

(本篇完)