2016-11-05 3 views
2

В elisp, как я могу получить привязку деструктурирования для совпадений регулярных выражений?Разрушение связывания для совпадений регулярных выражений

Например,

;; what is the equivalent of this with destructuring? 
(with-temp-buffer 
    (save-excursion (insert "a b")) 
    (re-search-forward "\\(a\\) \\(b\\)") 
    (cons (match-string 1) 
     (match-string 2))) 

;; trying to do something like the following 
(with-temp-buffer 
    (save-excursion (insert "a b")) 
    (cl-destructuring-bind (a b) (re-search-forward "\\(a\\) \\(b\\)") 
    (cons a b))) 

Я думал, что я должен был бы написать макрос, чтобы расширить матчи, если нет другого пути.

+0

Я не думаю, что вопрос имеет смысл. '' re-search-forward' сам не возвращает структуру данных, которая имеет отношение к тому, что вы хотите сделать, поэтому я не вижу никакой цели, чтобы попытаться использовать деструктурирующее связывание здесь. Просто придерживайтесь своего исходного кода? – phils

ответ

3

Вот один из способов: сначала продлить pcase принять новый re-match шаблон, с определением таких как:

(pcase-defmacro re-match (re) 
    "Matches a string if that string matches RE. 
RE should be a regular expression (a string). 
It can use the special syntax \\(?VAR: to bind a sub-match 
to variable VAR. All other subgroups will be treated as shy. 

Multiple uses of this macro in a single `pcase' are not optimized 
together, so don't expect lex-like performance. But in order for 
such optimization to be possible in some distant future, back-references 
are not supported." 
    (let ((start 0) 
     (last 0) 
     (new-re '()) 
     (vars '()) 
     (gn 0)) 
    (while (string-match "\\\\(\\(?:\\?\\([-[:alnum:]]*\\):\\)?" re start) 
     (setq start (match-end 0)) 
     (let ((beg (match-beginning 0)) 
      (name (match-string 1 re))) 
     ;; Skip false positives, either backslash-escaped or within [...]. 
     (when (subregexp-context-p re start last)   
      (cond 
      ((null name) 
      (push (concat (substring re last beg) "\\(?:") new-re)) 
      ((string-match "\\`[0-9]" name) 
      (error "Variable can't start with a digit: %S" name)) 
      (t 
      (let* ((var (intern name)) 
        (id (cdr (assq var vars)))) 
       (unless id 
       (setq gn (1+ gn)) 
       (setq id gn) 
       (push (cons var gn) vars)) 
       (push (concat (substring re last beg) (format "\\(?%d:" id)) 
        new-re)))) 
      (setq last start)))) 
    (push (substring re last) new-re) 
    (setq new-re (mapconcat #'identity (nreverse new-re) "")) 
    `(and (pred stringp) 
      (app (lambda (s) 
       (save-match-data 
        (when (string-match ,new-re s) 
        (vector ,@(mapcar (lambda (x) `(match-string ,(cdr x) s)) 
             vars))))) 
       (,'\` [,@(mapcar (lambda (x) (list '\, (car x))) vars)]))))) 

и как только это будет сделано, вы можете использовать его следующим образом:

(pcase X 
    ((re-match "\\(?var:[[:alpha:]]*\\)=\\(?val:.*\\)") 
    (cons var val))) 

или

(pcase-let 
    (((re-match "\\(?var:[[:alpha:]]*\\)=\\(?val:.*\\)") X)) 
    (cons var val)) 

Это не был тщательно протестирован, и, как упоминалось в docstring, он работает не так эффективно, как это (c | sh), когда вы одновременно сопоставляете строку с различными регулярными выражениями. Также вы получаете только подстроки, а не их позицию. И, наконец, он применяет поиск регулярного выражения к строке, тогда как в manny/most случаях регулярные выражения используются в буфере. Но вы все равно можете найти это полезным.

+0

Огромный ответ :) Большое спасибо – jenesaisquoi

Смежные вопросы