Spec-Zone.ru › Elisp

Определение новых форм rx

Обозначение rx может быть расширено путём определения новых символов и параметризованных форм через другие rx выражения. Это удобно для совместного использования частей между несколькими выражениями regex и для упрощения создания и понимания сложных выражений, объединяя их из меньших частей.

Например, вы могли бы определить name как (one-or-more letter), а (quoted x) как (seq ?' x ?') для любого x. Эти формы затем можно использовать в rx выражениях, как и любые другие: (rx (quoted name)) будет соответствовать непустой последовательности букв внутри одинарных кавычек.

Нижеследующие макросы Lisp предоставляют различные способы привязки имён к определениям. Общим для всех из них являются следующие правила:

  • Встроенные rx формы, такие как digit и group, не могут быть переопределены.
  • Определения хранятся в своём собственном пространстве имён, отделённом от пространства имён переменных Lisp. Таким образом, нет необходимости добавлять суффикс, подобный -regexp к именам; они не могут конфликтовать ни с чем другим.
  • Определения не могут ссылаться на себя рекурсивно, прямо или косвенно. Если вам это нужно, вам нужен парсер, а не регулярное выражение.
  • Определения расширяются только в вызовах rx или rx-to-string, а не просто наличием их в макросах определения. Это означает, что порядок определений не имеет значения, даже когда они ссылаются друг на друга, и что синтаксические ошибки появляются только при использовании, а не при определении.
  • Пользовательские формы разрешены везде, где ожидаются произвольные rx выражения; например, в теле формы zero-or-one, но не внутри any или category форм. Они также разрешены внутри not и intersection форм.
Макрос: rx-define name [arglist] rx-form

Определяет name глобально во всех последующих вызовах rx и rx-to-string. Если arglist отсутствует, тогда name определяется как простой символ, который будет заменён на rx-form. Пример:

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

Если arglist присутствует, он должен быть списком из нуля или более имён аргументов, и тогда name определяется как параметризованная форма. При использовании в rx выражении как (name arg…), каждый arg заменит соответствующее имя аргумента внутри rx-form.

arglist может заканчиваться &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…

Делает rx определения в bindings доступными локально для rx вызовов макросов в body, который затем оценивается.

Каждый элемент 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, и вычисляет body с этими привязками, действующими для вызовов rx-to-string.

Этот макрос похож на 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.

Copyright © 1990-1996, 1998-2022 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Extending-Rx.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API