R6RS:翻訳:Standard Libraries:12.4 Parsing input and producing output

R6RS:翻訳:Standard Libraries:12.4 Parsing input and producing output

12.4 入力の解析と出力の生成

変換子は入力を syntax-case で分解し、出力を syntax で再構成する。

[syntax] (syntax-case <expression> (<literal> ...) <syntax-case clause> ...)

[auxiliary syntax] _

[auxiliary syntax] ...

構文: 各 <literal> は識別子であり、 <clause> は以下のふたつのうちのいずれかの形式でなければならない。

(<pattern> <output expression>)
(<pattern> <fender> <output expression>)

<Fender> と <output expression> は <expression> である。

<pattern> は、識別子、定数、ないしは以下のいずれかである。

(<pattern> ...)
(<pattern> <pattern> ... . <pattern>)
(<pattern> ... <pattern> <ellipsis> <pattern> ...)
(<pattern> ... <pattern> <ellipsis> <pattern> ... . <pattern>)
#(<pattern> ...)
#(<pattern> ... <pattern> <ellipsis> <pattern> ...)

<ellipsis> は識別子 "..." (ピリオドみっつ)である。

pattern にあらわれる識別子は下線(_)やリテラルのリスト (<literal> ...)、省略記号(...)であってもかまわない。 それ以外の識別子はパターン変数である。 省略記号や下線が (<literal> ...) 中に現れた場合は構文違反である。

_ と ... については (rnrs base (6)) ライブラリと同様である。

パターン変数は入力中の任意の下位フォームにマッチし、入力のその部分を参照するのに使われる。 同一のパターン変数が <pattern> 中に複数回現れるのは構文違反である。

下線も任意の下位フォームにマッチするが、これはパターン変数ではなく、入力の要素を参照するのには使えない。 <pattern> 中に下線が複数回あらわれてもよい。

リテラル識別子は、入力の部分フォームが識別子で、かつ入力中にあらわれたものとリテラルのリストにあらわれたものが同一の静的束縛をもつ場合か、ふたつの識別子の名前が同一で、どちらも静的に束縛されていない場合、かつまたその場合にかぎり、入力フォームとマッチする。

省略記号が後続するパターンは入力の要素 0 個以上にマッチする。

より形式的に言うと、入力フォーム F がパターン P にマッチする必要十分条件は以下の場合である。

意味論: syntax-case ではまず <expression> を評価する。次にその値を <syntax-case clause> の最初の <pattern> から順にマッチさせていく。 このとき、マッチの必要に応じて評価結果のラップははずされる。 パターンが値にマッチし、<fender> のない場合は、 <output expression> が評価され、その値が syntax-case 式の値として返される。 パターンが値にマッチしなかった場合、二番目の <syntax-case clause>、 三番目の <syntax-case clause> と、順に試していく。 値がいずれのパターンともマッチしなかった場合には構文違反となる。

省略可能な <fender> があった場合、その節を受理するかどうか評価する追加の基準になる。 与えられた <syntax-case clause> の <pattern> が入力にマッチした場合、対応する <fender> が評価される。 <fender> を評価した値が真だった場合、その節は受理される。 さもなくは、パターンがマッチしなかった場合と同様、その節は棄却される。 フェンダーは論理的にはマッチング処理の一部である。 つまり、入力の基本的な構造以外の追加的な基準を指定するのである。

節の <pattern> 中のパターン変数は、<fender>(もしあれば)と <output expression> 中では、入力値の対応する部分に束縛される。 パターン変数は syntax 式(下記参照)のなかでだけ参照することができる。 パターン変数はプログラム中の変数やキーワードと同じ名前空間に格納される。

syntax-case フォームが末尾文脈にあった場合、<output expresions> もまた末尾位置にある。

[syntax] (syntax <template>)

: #'<template> は (syntax template) と等価である。

syntax 式は quote 式とほぼ同じである。 ただし、 (1) <template> 中のパターン変数の値は <template> に挿入される。 (2) 入力とテンプレート中の文脈情報は静的スコープ用に保持される。 (3) syntax 式の値は構文オブジェクトである。

<template> はパターン変数か、パターン変数でない識別子か、パターンデータか、以下のいずれかである。

(<subtemplate> ...)
(<subtemplate> ... . <template>)
#(<subtemplate> ...)

<subtemplate> は <template> に 0 個以上の省略記号が続いたものである。

syntax の値は <template> のコピーのパターン変数の部分を、入力のなかの対応する部分フォームで置き換えたものになる。 パターン変数ではないパターンデータと識別子は出力にそのままコピーされる。 省略記号の前の部分テンプレートは 0 個以上の部分テンプレートの出現に展開される。 このとき、部分テンプレートは省略記号の続く部分テンプレートのパターン変数を最低でもひとつは含まなければならない (そうしないと、部分フォームが出力中で何度繰り返されるのか展開器が決定できない)。 省略記号の続く部分パターンのパターン変数は(最低でも)入力と同じ数だけの省略記号の続く部分テンプレートの中にだけ現れる。 このようなパターン変数は、出力では、入力の部分フォームを指定通り束縛、分配したものに置き換えられる。 パターン変数に対応するパターンよりも多くの省略記号の続いた場合、入力は必要に応じて複製される。部分テンプレートは省略記号の続く部分パターン内のパターン変数を少なくともひとつ含まなければならない。また、そのようなパターン変数の少なくともひとつについて、部分テンプレートにはパターン変数の現れた部分パターンとちょうど同じ数の省略記号が続かなければならない(さもなくは、展開器は部分フォームを出力中で何度繰り返せばよいか決定できない)。この段落で述べた制約条件が満たされない場合は構文違反となる。

(<ellipsis> <template>) 形式のテンプレートは省略記号に特別な意味がないことを除けば template と同じである。 つまり、<template> に含まれる省略記号はすべてふつうの識別子としてあつかわれるのである。 特に (... ...) は省略記号 ... そのものになる。 これによって、省略記号を含むフォームを出力するマクロ展開を書けるようになる。

syntax の出力の有無は以下の規則によって決定される。

対応する部分フォームがラップされていた場合、かつまたその場合にかぎり、パターン変数の部分に挿入された入力部分フォームはラップされる。

以下の定義で syntax-case と syntax を概観する。二番目は最初のものと同等であるが、syntax フォームを省略せずに書くのではなく、接頭辞の #' を使っている。

(define-syntax or
  (lambda (x)
    (syntax-case x ()
      [(_) (syntax #f)]
      [(_ e) (syntax e)]
      [(_ e1 e2 e3 ...)
       (syntax (let ([t e1])
                 (if t t (or e2 e3 ...))))])))
(define-syntax or
  (lambda (x)
    (syntax-case x ()
      [(_) #’#f]
      [(_ e) #’e]
      [(_ e1 e2 e3 ...)
       #’(let ([t e1])
           (if t t (or e2 e3 ...)))])))

次の例では識別子マクロを定義している。 これはリスト構造の最初の部分にキーワードへの参照の現れないマクロの使用である。 ふたつめの例では make-variable-transformer を使い、キーワードが set! 式の左辺にあらわれた場合の処理をしている。

(define p (cons 4 5))
(define-syntax p.car
  (lambda (x)
    (syntax-case x ()
      [(_ . rest) #’((car p) . rest)]
      [_  #’(car p)])))
p.car         ⇒ 4
(set! p.car 15)         ⇒  &syntax exception
(define p (cons 4 5))
(define-syntax p.car
  (make-variable-transformer
    (lambda (x)
      (syntax-case x (set!)
        [(set! _ e) #’(set-car! p e)]
        [(_ . rest) #’((car p) . rest)]
        [_  #’(car p)]))))
(set! p.car 15)
p.car                   ⇒ 15
p                       ⇒ (15 . 5)
More ...