Определение новых форм 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