Регулярные выражения
Как можно использовать регулярные выражения?
Хотя реализации не предоставляют такой возможности, существует хорошая библиотека CL-PPCRE оптимизированная по скорости, и позволяющая использовать регулярные выражения, как они используются в Perl.
CL-PPCRE ("Common Lisp Portable Perl Compatible Regular Expression") отличают следующие свойства:
- Сами регулярные выражения совместимы с Перл-овыми.
- Библиотека работает для разных ANSI реализаций CL.
- Есть альтернативная запись регулярных выражений в виде s-выражений.
- Работает быстро, как было сказано.
- Библиотека свободна.
Про установку написано выше, в часте про ASDF.
Примеры использования:
(scan "b.r" "foo bar baz bur" :start 2 :end 10)
; вернёт 5 и 7
функция scan ищет во второй строке начиная с индекса start и до индекса end совпадения с RE, определённым в первой строке, при отсутствии совпадений возвращает NIL, иначе - индексы начала и конца первого совпадения.
Допустим стоит задача переводить текст между \=\=\=\=, \=\=\= и \=\= в html заголовки h1, h2 и h3 соответственно. Этого можно достичь трёмя строчка - по одной на каждую задачу:
(setf *str* (regex-replace-all "\=\=\=\=(.*)=\=\=\=\=" *str* "<h1>\\1</h1>" :preserve-case t))
(setf *str* (regex-replace-all "\=\=\=(.*)\=\=\=" *str* "<h2>\\1</h2>" :preserve-case t))
(setf *str* (regex-replace-all "\=\=(.*)\=\=" *str* "<h3>\\1</h3>" :preserve-case t))
; Как видим, перловые $1, $2, $3, ... для запоминания паттернов в скобках () заменяются переменными \\1, \\2, \\3, ...
Вот тут FIXME , я этот код не проверял.
Перевод объявлений #define из .h файлов
(defun h-to-lisp (f.h f.lisp)
"Перевод однострочных объявлений #define из .h файлов"
(let ((in (open f.h :if-does-not-exist nil)) (out (open f.lisp :direction :output :if-exists :supersede)))
(when in
(loop for line = (read-line in nil)
while line do (if (scan "#define\\s+\\S+\\s+\\S+" line)
(format out "~a~%"
(string-downcase ;; уменьшаем регистр.
(substitute #\- #\_ ;; Вместо _ ставим - .
(regex-replace "#\\s*define\\s+(\\S+)\\s+(\\S+).*" line "(defconstant +\\1+ \\2)"))))))
(close in)
(close out))))
Может оказаться, что вторым значением в #define будет 8 или 16-ричное число, которое записывается в Си и в CL по-разному, поэтому можно встроить в h-to-lisp такую функцию:
(defun parse-c-token (token)
(cond ((scan "0+[0-9]+" token) (read-from-string (regex-replace "0+([0-9]+)" token "#o\\1")))
((scan "0x[0-9|a-f]+" token) (read-from-string (regex-replace "0x([0-9|a-f]+)" token "#x\\1")))
((scan "[0-9]+l" token) (read-from-string (regex-replace "([0-9|\.]+)l" token "\\1")))
((scan "'\\S+'" token) (read-from-string (regex-replace "'(\\S+)'" token "#\\\1")))
(t token)))