rxНотация rx может быть расширена путём определения новых символов и
параметризованных форм через другие выражения rx. Это
удобно для совместного использования частей между несколькими регулярными выражениями, а также для упрощения
создания и понимания сложных выражений путём их сборки из более мелких
частей.
Например, вы можете определить name как
(one-or-more letter), а (quoted x) как
(seq ?' x ?') для любого x. Эти формы затем можно
использовать в выражениях rx как любые другие: (rx (quoted name))
будет соответствовать непустой последовательности букв внутри одинарных кавычек.
Макросы Lisp, приведённые ниже, предоставляют различные способы привязки имён к определениям. Для всех них действуют следующие правила:
rx, такие как digit и group, нельзя
переопределять.
-regexp к именам; они не могут конфликтовать ни с чем другим.
rx или
rx-to-string, а не просто по факту их наличия в макросах
определения. Это означает, что порядок определений не имеет значения, даже
когда они ссылаются друг на друга, и что синтаксические ошибки проявляются
только при их использовании, а не при определении.
rx; например, в теле формы
zero-or-one, но не внутри форм any или category.
Они также допускаются внутри форм not и
intersection.
Определяет имя глобально во всех последующих вызовах rx и
rx-to-string. Если список-аргументов отсутствует, то имя
определяется как простой символ, заменяемый на rx-форму. Пример:
(rx-define haskell-comment (seq "--" (zero-or-more nonl)))
(rx haskell-comment)
⇒ "--.*"
Если список-аргументов присутствует, он должен быть списком из нуля или
более имён аргументов, а имя тогда определяется как параметризованная
форма. При использовании в выражении rx как (имя арг…),
каждый арг заменяет соответствующее имя аргумента внутри
rx-формы.
список-аргументов может заканчиваться на &rest и одно последнее имя аргумента,
обозначающее параметр остатка. Параметр остатка раскрывается во все
дополнительные фактические значения аргументов, не сопоставленные ни с каким другим параметром в
списке-аргументов, вставляясь в rx-форму в место своего появления. Пример:
(rx-define moan (x y &rest r) (seq x (one-or-more y) r "!"))
(rx (moan "MOO" "A" "MEE" "OW"))
⇒ "MOOA+MEEOW!"
Поскольку определение глобально, рекомендуется давать имени префикс пакета во избежание конфликтов имён с определениями в других местах, как это обычно делается при именовании нелокальных переменных и функций.
Формы, определённые таким образом, выполняют только простую подстановку шаблонов.
Для произвольных вычислений используйте их вместе с формами 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 из привязок локально доступными для
вызовов макроса rx в теле, которое затем вычисляется.
Каждый элемент привязок имеет форму
(имя [список-аргументов] rx-форма), где части
имеют тот же смысл, что и в rx-define выше. Пример:
(rx-let ((comma-separated (item) (seq item (0+ "," item)))
(number (1+ digit))
(numbers (comma-separated number)))
(re-search-forward (rx "(" numbers ")")))
Определения доступны только во время макро-раскрытия тела и, следовательно, не присутствуют во время выполнения скомпилированного кода.
rx-let можно использовать не только внутри функции, но и на верхнем
уровне для включения глобальных определений переменных и функций, которым
требуется общий набор форм rx. Поскольку имена являются локальными
внутри тела, нет необходимости в каких-либо префиксах пакетов.
Пример:
(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 лексическая, что означает, что
они не видны за пределами самого тела, даже в функциях,
вызываемых из тела.
Вычисляет привязки в список привязок, как в rx-let,
и вычисляет тело с этими привязками, действующими для вызовов
rx-to-string.
Этот макрос похож на rx-let, за исключением того, что аргумент привязки
вычисляется (и поэтому его нужно кавычить, если это литерал списка),
а определения подставляются во время выполнения, что
требуется для работы 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 состоит в том, что привязки
имеют динамическую область видимости и, следовательно, также доступны в функциях, вызываемых из
тела. Однако они не видны внутри функций, определённых в
теле.