Download
(defvar cexp-start "!(" "Start control string of balanced expression within regular expression.")
(defvar cexp-end "!)" "End control string of balanced expression within regular expression.")
(defun cexp-find-special (cexp &optional cexp-re start)
"Finds balanced special strings for sexp within cexp. Returns
cons with start position of the special string and the special
string itself."
(or cexp-re (setq cexp-re (concat "\\(" cexp-start "\\|" cexp-end "\\)")))
(while (and (setq start (string-match (concat "\\\\" cexp-re) cexp start))
(> start 0)
(= ?\\ (elt cexp (1- start))))
(setq start (1+ start)))
(cons start (match-string-no-properties 1 cexp)))
(defun cexp-find-top-sexp (cexp &optional start)
"Returns a cons (BEG . END) of the beginning position BEG and the end position END of the first
top-level sexp control string in CEXP.
The sexp control string is delimited by the strings defined in `cexp-start' and `cexp-end'.
BEG points to the start string `cexp-start' within cexp and END points at the character behind end string `cexp-end' or to the end of CEXP.
"
(let* (
(cexp-re (concat "\\(" cexp-start "\\|" cexp-end "\\)"))
(c (cexp-find-special cexp cexp-re start))
(b (car c))
)
(if (setq start b)
(progn
(setq start (1+ start))
(unless (string= (cdr c) cexp-start) (error "Unbalanced cexp."))
(let ((cnt 1))
(while (and
(> cnt 0)
(car (setq c (cexp-find-special cexp cexp-re start))))
(if (string= (cdr c) cexp-start)
(setq cnt (1+ cnt))
(setq cnt (1- cnt)))
(setq start (1+ (car c))))
(when (> cnt 0) (error "Unbalanced cexp %d" cnt)))
(cons b (+ (car c) 1 (length cexp-end))))
nil)))
(defun search-forward-cexp1 (cexp &optional var-match-data)
"Search for combined regular and balanced expressions (cexp). May fail after partial match.
The syntax of cexp is almost that of a regular expression with the exception that the string \\!( introduces a balanced expression and \\!) closes a balanced expression. The matched balanced expressions appear within the match data.
Regular expression braces \\( and \\) may not include balanced expressions (sexps)."
(let* ((sexpBegEnd (cexp-find-top-sexp cexp))
(endRegexp (if sexpBegEnd (car sexpBegEnd)))
(re (substring cexp 0 endRegexp))
)
(when (search-forward-regexp re nil 'noError)
(setq var-match-data (append var-match-data (match-data)))
(if sexpBegEnd
(let
((buf-sexpBeg (point))
(buf-sexpEnd (goto-char (scan-sexps (point) 1)))
)
(setq var-match-data (append var-match-data (list buf-sexpBeg buf-sexpEnd)))
(save-excursion
(save-restriction
(narrow-to-region buf-sexpBeg buf-sexpEnd)
(goto-char (point-min))
(setq var-match-data (search-forward-cexp1 (substring cexp (+ (length cexp-start) (car sexpBegEnd) 1) (- (cdr sexpBegEnd) (length cexp-end) 1)) var-match-data))
))
(when (and var-match-data (not (= (length cexp) (cdr sexpBegEnd))))
(setq var-match-data (search-forward-cexp1 (substring cexp (cdr sexpBegEnd)) var-match-data))
)))
var-match-data)))
(defun search-forward-cexp (cexp)
"Search for combined regular and balanced expressions (cexp).
The syntax of cexp is almost that of a regular expression with the exception that the string \\!( introduces a balanced expression and \\!) closes a balanced expression. The matched balanced expressions appear within the match data.
Regular expression braces \\( and \\) may not include balanced expressions (sexps)."
(let (var-match-data)
(while (not (or (setq var-match-data (search-forward-cexp1 cexp)) (eobp))))
var-match-data))
(provide 'cexp)