GNU Emacs 32.0.50 (9008bf64c3) のマニュアルを PLaMo 翻訳で機械翻訳したものです。誤訳を含むことがあります。原文: https://www.gnu.org/software/emacs/manual/html_node/elisp/Extending-Rx.html ・ emacs-doc.conao3.com

35.3.3.3 新しい rx フォームの定義

rx 記法は、他の rx 式を用いて新しい記号やパラメータ化された形式を定義することで、拡張可能です。これは、複数の正規表現間で部分を共有したり、複雑な正規表現を小さな構成要素から組み合わせることで、構築や理解を容易にしたりする際に便利です。

例えば、任意の x に対して、name を (one-or-more letter) の意味に、(quoted x) を (seq ?' x ?') の意味に定義することができます。これらの形式は、他の形式と同様に rx 式で使用でき、例えば (rx (quoted name)) はシングルクォートで囲まれた空でない文字シーケンスにマッチします。

以下のLispマクロは、名前を定義に結びつけるためのさまざまな方法を提供します。これらすべてに共通するルールは以下の通りです。

マクロ: rx-define name [arglist] rx-form

それ以降の rx および rx-to-string のすべての呼び出しにおいて、name をグローバルに定義します。arglist が存在しない場合、name は rx-form に置き換えられる通常のシンボルとして定義されます。例:

(rx-define haskell-comment (seq "--" (zero-or-more nonl)))
(rx haskell-comment)
     ⇒ "--.*"

arglist が存在する場合、それは0個以上の引数名のリストである必要があり、name はパラメータ化された形式として定義されます。rx 式の中で (name arg…) として使用される場合、各 arg は rx-form 内の対応する引数名を置き換えます。

arglist は &rest と最後の引数名で終了する場合があり、これは rest パラメータを表します。rest パラメータは、arglist の他のどのパラメータにも一致しなかったすべての追加の実引数値に展開され、rx-form の該当する箇所に挿入されます。例:

(rx-define moan (x y &rest r) (seq x (one-or-more y) r "!"))
(rx (moan "MOO" "A" "MEE" "OW"))
     ⇒ "MOOA+MEEOW!"

定義はグローバルであるため、非ローカル変数や関数に名前を付ける際の慣例に従い、他の箇所の定義との名前の衝突を避けるため、name にパッケージプレフィックスを付与することが推奨されます。

このように定義されたフォームは、単純なテンプレート置換のみを行います。任意の計算を行う場合は、rx フォームを eval、regexp または literal と組み合わせて使用してください。例:

(defun n-tuple-rx (n element)
  `(seq "<"
        (group-n 1 ,element)
        ,@(mapcar (lambda (i) `(seq ?, (group-n ,i ,element)))
                  (number-sequence 2 n))
        ">"))
(rx-define n-tuple (n element) (eval (n-tuple-rx n 'element)))
(rx (n-tuple 3 (+ (in "0-9"))))
  ⇒ "<\\(?1:[0-9]+\\),\\(?2:[0-9]+\\),\\(?3:[0-9]+\\)>"
マクロ: rx-let (bindings…) body…

Make the rx definitions in bindings available locally for rx macro invocations in body, which is then evaluated.

bindings の各要素は形式 (name [arglist] rx-form) に従っており、各部分は上記の rx-define と同様の意味を持ちます。例:

(rx-let ((comma-separated (item) (seq item (0+ "," item)))
         (number (1+ digit))
         (numbers (comma-separated number)))
  (re-search-forward (rx "(" numbers ")")))

定義はbodyのマクロ展開時にのみ利用可能であるため、コンパイル済みコードの実行時には存在しません。

rx-letは関数内だけでなく、トップレベルでも使用でき、共通のrx形式セットを共有する必要があるグローバル変数や関数の定義を含めることができます。名前はbody内ではローカルであるため、パッケージプレフィックスは不要です。例:

(rx-let ((phone-number (seq (opt ?+) (1+ (any digit ?-)))))
  (defun find-next-phone-number ()
    (re-search-forward (rx phone-number)))
  (defun phone-number-p (string)
    (string-match-p (rx bos phone-number eos) string)))

rx-let バインディングのスコープはレキシカルであり、つまり body から呼び出された関数内であっても、body の外部からは参照できません。

マクロ: rx-let-eval bindings body…

bindings を rx-let と同様に束縛のリストとして評価し、rx-to-string の呼び出しに対しては、それらの束縛が有効である状態で body を評価する。

このマクロは rx-let と似ていますが、bindings 引数が評価される点(そのため、リストリテラルの場合はクォートする必要があります)と、定義が実行時に置換される点が異なります。rx-to-string を動作させるには、この置換が必要です。例:

(rx-let-eval
    '((ponder (x) (seq "Where have all the " x " gone?")))
  (looking-at (rx-to-string
               '(ponder (or "flowers" "young girls"
                            "left socks")))))

rx-letとのもう一つの違いは、bindingsが動的にスコープされるため、bodyから呼び出される関数内でも利用可能である点です。ただし、bodyで定義された関数内では参照できません。