| [6] | 1 | ;;; -*- Log: Hemlock.Log; Package: Hemlock-Internals -*-
|
|---|
| 2 | ;;;
|
|---|
| 3 | ;;; **********************************************************************
|
|---|
| 4 | ;;; This code was written as part of the CMU Common Lisp project at
|
|---|
| 5 | ;;; Carnegie Mellon University, and has been placed in the public domain.
|
|---|
| 6 | ;;;
|
|---|
| 7 | #+CMU (ext:file-comment
|
|---|
| 8 | "$Header$")
|
|---|
| 9 | ;;;
|
|---|
| 10 | ;;; **********************************************************************
|
|---|
| 11 | ;;;
|
|---|
| 12 | ;;; Searching and replacing functions for Hemlock.
|
|---|
| 13 | ;;; Originally written by Skef Wholey, Rewritten by Rob MacLachlan.
|
|---|
| 14 | ;;;
|
|---|
| 15 |
|
|---|
| 16 | (in-package :hemlock-internals)
|
|---|
| 17 |
|
|---|
| 18 | |
|---|
| 19 |
|
|---|
| 20 | ;;; The search pattern structure is used only by simple searches, more
|
|---|
| 21 | ;;; complex ones make structures which include it.
|
|---|
| 22 |
|
|---|
| 23 | (defstruct (search-pattern (:print-function %print-search-pattern)
|
|---|
| 24 | (:constructor internal-make-search-pattern))
|
|---|
| 25 | kind ; The kind of pattern to search for.
|
|---|
| 26 | direction ; The direction to search in.
|
|---|
| 27 | pattern ; The search pattern to use.
|
|---|
| 28 | search-function ; The function to call to search.
|
|---|
| 29 | reclaim-function) ; The function to call to reclaim this pattern.
|
|---|
| 30 |
|
|---|
| 31 | (setf (documentation 'search-pattern-p 'function)
|
|---|
| 32 | "Returns true if its argument is a Hemlock search-pattern object,
|
|---|
| 33 | Nil otherwise.")
|
|---|
| 34 |
|
|---|
| 35 | (defun %print-search-pattern (object stream depth)
|
|---|
| 36 | (let ((*print-level* (and *print-level* (- *print-level* depth)))
|
|---|
| 37 | (*print-case* :downcase))
|
|---|
| 38 | (declare (special *print-level* *print-case*))
|
|---|
| 39 | (write-string "#<Hemlock " stream)
|
|---|
| 40 | (princ (search-pattern-direction object) stream)
|
|---|
| 41 | (write-char #\space stream)
|
|---|
| 42 | (princ (search-pattern-kind object) stream)
|
|---|
| 43 | (write-string " Search-Pattern for ")
|
|---|
| 44 | (prin1 (search-pattern-pattern object) stream)
|
|---|
| 45 | (write-char #\> stream)
|
|---|
| 46 | (terpri stream)))
|
|---|
| 47 |
|
|---|
| 48 | (defvar *search-pattern-experts* (make-hash-table :test #'eq)
|
|---|
| 49 | "Holds an eq hashtable which associates search kinds with the functions
|
|---|
| 50 | that know how to make patterns of that kind.")
|
|---|
| 51 | (defvar *search-pattern-documentation* ()
|
|---|
| 52 | "A list of all the kinds of search-pattern that are defined.")
|
|---|
| 53 |
|
|---|
| 54 | ;;; define-search-kind -- Internal
|
|---|
| 55 | ;;;
|
|---|
| 56 | ;;; This macro is used to define a new kind of search pattern. Kind
|
|---|
| 57 | ;;; is the kind of search pattern to define. Lambda-list is the argument
|
|---|
| 58 | ;;; list for the expert-function to be built and forms it's body.
|
|---|
| 59 | ;;; The arguments passed are the direction, the pattern, and either
|
|---|
| 60 | ;;; an old search-pattern of the same type or nil. Documentation
|
|---|
| 61 | ;;; is put on the search-pattern-documentation property of the kind
|
|---|
| 62 | ;;; keyword.
|
|---|
| 63 | ;;;
|
|---|
| [12635] | 64 | (defmacro define-search-kind (kind lambda-list documentation &body forms)
|
|---|
| 65 | `(progn
|
|---|
| 66 | (push ,documentation *search-pattern-documentation*)
|
|---|
| 67 | (setf (gethash ,kind *search-pattern-experts*)
|
|---|
| [6] | 68 | #'(lambda ,lambda-list ,@forms))))
|
|---|
| 69 | |
|---|
| 70 |
|
|---|
| 71 | ;;; new-search-pattern -- Public
|
|---|
| 72 | ;;;
|
|---|
| 73 | ;;; This function deallocates any old search-pattern and then dispatches
|
|---|
| 74 | ;;; to the correct expert.
|
|---|
| 75 | ;;;
|
|---|
| 76 | (defun new-search-pattern (kind direction pattern &optional
|
|---|
| 77 | result-search-pattern)
|
|---|
| 78 | "Makes a new Hemlock search pattern of kind Kind to search direction
|
|---|
| 79 | using Pattern. Direction is either :backward or :forward.
|
|---|
| 80 | If supplied, result-search-pattern is a pattern to destroy to make
|
|---|
| 81 | the new one. The variable *search-pattern-documentation* contains
|
|---|
| 82 | documentation for each kind."
|
|---|
| 83 | (unless (or (eq direction :forward) (eq direction :backward))
|
|---|
| 84 | (error "~S is not a legal search direction." direction))
|
|---|
| 85 | (when result-search-pattern
|
|---|
| 86 | (funcall (search-pattern-reclaim-function result-search-pattern)
|
|---|
| 87 | result-search-pattern)
|
|---|
| 88 | (unless (eq kind (search-pattern-kind result-search-pattern))
|
|---|
| 89 | (setq result-search-pattern nil)))
|
|---|
| 90 | (let ((expert (gethash kind *search-pattern-experts*)))
|
|---|
| 91 | (unless expert
|
|---|
| 92 | (error "~S is not a defined search pattern kind." kind))
|
|---|
| [12170] | 93 | (funcall expert direction pattern result-search-pattern)))
|
|---|
| 94 |
|
|---|
| 95 | (defun new-search-vector (vec access-fn)
|
|---|
| 96 | (let* ((max 0))
|
|---|
| 97 | (declare (fixnum max))
|
|---|
| 98 | (dotimes (i (length vec))
|
|---|
| 99 | (let* ((code (funcall access-fn vec i)))
|
|---|
| 100 | (when (> code max)
|
|---|
| [6] | 101 | (setq max code))))
|
|---|
| 102 | (make-array (the fixnum (1+ max)))))
|
|---|
| 103 |
|
|---|
| 104 | (eval-when (:compile-toplevel :execute)
|
|---|
| [12170] | 105 |
|
|---|
| [6] | 106 | (defmacro dispose-search-vector (vec)
|
|---|
| 107 | vec)
|
|---|
| 108 | ); eval-when (:compile-toplevel :execute)
|
|---|
| 109 | |
|---|
| 110 |
|
|---|
| 111 | ;;;; macros used by various search kinds:
|
|---|
| 112 |
|
|---|
| 113 | ;;; search-once-forward-macro -- Internal
|
|---|
| 114 | ;;;
|
|---|
| 115 | ;;; Passes search-fun strings, starts and lengths to do a forward
|
|---|
| 116 | ;;; search. The other-args are passed through to the searching
|
|---|
| 117 | ;;; function after after everything else The search-fun is
|
|---|
| 118 | ;;; expected to return NIL if nothing is found, or it index where the
|
|---|
| 119 | ;;; match ocurred. Something non-nil is returned if something is
|
|---|
| 120 | ;;; found and line and start are set to where it was found.
|
|---|
| 121 | ;;;
|
|---|
| 122 | (defmacro search-once-forward-macro (line start search-fun &rest other-args)
|
|---|
| 123 | `(do* ((l ,line)
|
|---|
| 124 | (chars (line-chars l) (line-chars l))
|
|---|
| 125 | (len (length chars) (length chars))
|
|---|
| 126 | (start-pos ,start 0)
|
|---|
| 127 | (index 0))
|
|---|
| 128 | (())
|
|---|
| 129 | (declare (simple-string chars) (fixnum start-pos len)
|
|---|
| 130 | (type (or fixnum null) index))
|
|---|
| 131 | (setq index (,search-fun chars start-pos len ,@other-args))
|
|---|
| 132 | (when index
|
|---|
| 133 | (setq ,start index ,line l)
|
|---|
| 134 | (return t))
|
|---|
| 135 | (setq l (line-next l))
|
|---|
| 136 | (when (null l) (return nil))))
|
|---|
| 137 |
|
|---|
| 138 |
|
|---|
| 139 | ;;; search-once-backward-macro -- Internal
|
|---|
| 140 | ;;;
|
|---|
| 141 | ;;; Like search-once-forward-macro, except it goes backwards. Length
|
|---|
| 142 | ;;; is not passed to the search function, since it won't need it.
|
|---|
| 143 | ;;;
|
|---|
| 144 | (defmacro search-once-backward-macro (line start search-fun &rest other-args)
|
|---|
| 145 | `(do* ((l ,line)
|
|---|
| 146 | (chars (line-chars l) (line-chars l))
|
|---|
| 147 | (start-pos (1- ,start) (1- (length chars)))
|
|---|
| 148 | (index 0))
|
|---|
| 149 | (())
|
|---|
| 150 | (declare (simple-string chars) (fixnum start-pos)
|
|---|
| 151 | (type (or fixnum null) index))
|
|---|
| 152 | (setq index (,search-fun chars start-pos ,@other-args))
|
|---|
| 153 | (when index
|
|---|
| 154 | (setq ,start index ,line l)
|
|---|
| 155 | (return t))
|
|---|
| 156 | (setq l (line-previous l))
|
|---|
| 157 | (when (null l) (return nil))))
|
|---|
| 158 |
|
|---|
| 159 | |
|---|
| 160 |
|
|---|
| 161 | ;;;; String Searches.
|
|---|
| 162 | ;;;
|
|---|
| 163 | ;;; We use the Boyer-Moore algorithm for string searches.
|
|---|
| 164 | ;;;
|
|---|
| 165 |
|
|---|
| 166 | ;;; sensitive-string-search-macro -- Internal
|
|---|
| 167 | ;;;
|
|---|
| 168 | ;;; This macro does a case-sensitive Boyer-Moore string search.
|
|---|
| 169 | ;;;
|
|---|
| 170 | ;;; Args:
|
|---|
| 171 | ;;; String - The string to search in.
|
|---|
| 172 | ;;; Start - The place to start searching at.
|
|---|
| 173 | ;;; Length - NIL if going backward, the length of String if going forward.
|
|---|
| 174 | ;;; Pattern - A simple-vector of characters. A simple-vector is used
|
|---|
| 175 | ;;; rather than a string because it is believed that simple-vector access
|
|---|
| 176 | ;;; will be faster in most implementations.
|
|---|
| 177 | ;;; Patlen - The length of Pattern.
|
|---|
| 178 | ;;; Last - (1- Patlen)
|
|---|
| 179 | ;;; Jumps - The jump vector as given by compute-boyer-moore-jumps
|
|---|
| 180 | ;;; +/- - The function to increment with, either + (forward) or -
|
|---|
| 181 | ;;; (backward)
|
|---|
| [12170] | 182 | ;;; -/+ - Like +/-, only the other way around.
|
|---|
| 183 | (eval-when (:compile-toplevel :execute)
|
|---|
| [6] | 184 | (defmacro sensitive-string-search-macro (string start length pattern patlen
|
|---|
| [12170] | 185 | last jumps +/- -/+)
|
|---|
| [6] | 186 | (let* ((jumpslen (gensym))
|
|---|
| 187 | (charcode (gensym)))
|
|---|
| 188 | `(do ((scan (,+/- ,start ,last))
|
|---|
| 189 | (,jumpslen (length ,jumps))
|
|---|
| 190 | (patp ,last))
|
|---|
| 191 | (,(if length `(>= scan ,length) '(minusp scan)))
|
|---|
| 192 | (declare (fixnum scan patp))
|
|---|
| 193 | (let ((char (schar ,string scan)))
|
|---|
| 194 | (cond
|
|---|
| 195 | ((char= char (svref ,pattern patp))
|
|---|
| 196 | (if (zerop patp)
|
|---|
| [12170] | 197 | (return scan)
|
|---|
| 198 | (setq scan (,-/+ scan 1) patp (1- patp))))
|
|---|
| 199 | (t
|
|---|
| 200 | ;; If mismatch consult jump table to find amount to skip.
|
|---|
| [6] | 201 | (let* ((,charcode (search-char-code char))
|
|---|
| 202 | (jump (if (< ,charcode ,jumpslen)
|
|---|
| 203 | (svref ,jumps ,charcode)
|
|---|
| 204 | ,patlen)))
|
|---|
| [12170] | 205 | (declare (fixnum jump))
|
|---|
| [6] | 206 | (if (> jump (- ,patlen patp))
|
|---|
| 207 | (setq scan (,+/- scan jump))
|
|---|
| 208 | (setq scan (,+/- scan (- ,patlen patp)))))
|
|---|
| 209 | (setq patp ,last)))))))
|
|---|
| 210 | |
|---|
| 211 |
|
|---|
| 212 | ;;; insensitive-string-search-macro -- Internal
|
|---|
| 213 | ;;;
|
|---|
| 214 | ;;; This macro is very similar to the case sensitive one, except that
|
|---|
| 215 | ;;; we do the search for a hashed string, and then when we find a match
|
|---|
| 216 | ;;; we compare the uppercased search string with the found string uppercased
|
|---|
| [12170] | 217 | ;;; and only say we win when they match too.
|
|---|
| 218 | ;;;
|
|---|
| 219 | (defmacro insensitive-string-search-macro (string start length pattern
|
|---|
| 220 | folded-string patlen last
|
|---|
| 221 | jumps +/- -/+)
|
|---|
| 222 | (let* ((jumpslen (gensym)))
|
|---|
| 223 | `(do ((scan (,+/- ,start ,last))
|
|---|
| 224 | (,jumpslen (length ,jumps))
|
|---|
| 225 | (patp ,last))
|
|---|
| 226 | (,(if length `(>= scan ,length) '(minusp scan)))
|
|---|
| 227 | (declare (fixnum scan patp))
|
|---|
| [6] | 228 | (let ((hash (search-hash-code (schar ,string scan))))
|
|---|
| 229 | (declare (fixnum hash))
|
|---|
| 230 | (cond
|
|---|
| 231 | ((= hash (the fixnum (svref ,pattern patp)))
|
|---|
| 232 | (if (zerop patp)
|
|---|
| 233 | (if (do ((i ,last (1- i)))
|
|---|
| 234 | (())
|
|---|
| [12170] | 235 | (when (char/=
|
|---|
| 236 | (search-char-upcase (schar ,string (,+/- scan i)))
|
|---|
| [6] | 237 | (schar ,folded-string i))
|
|---|
| [12170] | 238 | (return nil))
|
|---|
| 239 | (when (zerop i) (return t)))
|
|---|
| 240 | (return scan)
|
|---|
| 241 | (setq scan (,+/- scan ,patlen) patp ,last))
|
|---|
| 242 | (setq scan (,-/+ scan 1) patp (1- patp))))
|
|---|
| 243 | (t
|
|---|
| 244 | ;; If mismatch consult jump table to find amount to skip.
|
|---|
| [6] | 245 | (let ((jump (if (< hash ,jumpslen)
|
|---|
| 246 | (svref ,jumps hash)
|
|---|
| [12170] | 247 | ,patlen)))
|
|---|
| [6] | 248 | (declare (fixnum jump))
|
|---|
| 249 | (if (> jump (- ,patlen patp))
|
|---|
| 250 | (setq scan (,+/- scan jump))
|
|---|
| 251 | (setq scan (,+/- scan (- ,patlen patp)))))
|
|---|
| 252 | (setq patp ,last)))))))
|
|---|
| 253 | |
|---|
| 254 |
|
|---|
| 255 | ;;;; Searching for strings with newlines in them:
|
|---|
| 256 | ;;;
|
|---|
| 257 | ;;; Due to the buffer representation, search-strings with embedded
|
|---|
| 258 | ;;; newlines need to be special-cased. What we do is break
|
|---|
| 259 | ;;; the search string up into lines and then searching for a line with
|
|---|
| 260 | ;;; the correct prefix. This is actually a faster search.
|
|---|
| 261 | ;;; For this one we just have one big hairy macro conditionalized for
|
|---|
| 262 | ;;; both case-sensitivity and direction. Have fun!!
|
|---|
| 263 |
|
|---|
| 264 | ;;; newline-search-macro -- Internal
|
|---|
| 265 | ;;;
|
|---|
| 266 | ;;; Do a search for a string containing newlines. Line is the line
|
|---|
| 267 | ;;; to start on, and Start is the position to start at. Pattern and
|
|---|
| 268 | ;;; optionally Pattern2, are simple-vectors of things that represent
|
|---|
| 269 | ;;; each line in the pattern, and are passed to Test-Fun. Pattern
|
|---|
| 270 | ;;; must contain simple-strings so we can take the length. Test-Fun is a
|
|---|
| 271 | ;;; thing to compare two strings and see if they are equal. Forward-p
|
|---|
| 272 | ;;; tells whether to go forward or backward.
|
|---|
| 273 | ;;;
|
|---|
| 274 | (defmacro newline-search-macro (line start test-fun pattern forward-p
|
|---|
| 275 | &optional pattern2)
|
|---|
| 276 | `(let* ((patlen (length ,pattern))
|
|---|
| 277 | (first (svref ,pattern 0))
|
|---|
| 278 | (firstlen (length first))
|
|---|
| 279 | (l ,line)
|
|---|
| 280 | (chars (line-chars l))
|
|---|
| 281 | (len (length chars))
|
|---|
| 282 | ,@(if pattern2 `((other (svref ,pattern2 0)))))
|
|---|
| 283 | (declare (simple-string first chars) (fixnum firstlen patlen len))
|
|---|
| 284 | ,(if forward-p
|
|---|
| 285 | ;; If doing a forward search, go to the next line if we could not
|
|---|
| 286 | ;; match due to the start position.
|
|---|
| 287 | `(when (< (- len ,start) firstlen)
|
|---|
| 288 | (setq l (line-next l)))
|
|---|
| 289 | ;; If doing a backward search, go to the previous line if the current
|
|---|
| 290 | ;; line could not match the last line in the pattern, and then go
|
|---|
| 291 | ;; back the 1- number of lines in the pattern to avoid a possible
|
|---|
| 292 | ;; match across the starting point.
|
|---|
| 293 | `(let ((1-len (1- patlen)))
|
|---|
| 294 | (declare (fixnum 1-len))
|
|---|
| 295 | (when (< ,start (length (the simple-string
|
|---|
| 296 | (svref ,pattern 1-len))))
|
|---|
| 297 | (setq l (line-previous l)))
|
|---|
| 298 | (dotimes (i 1-len)
|
|---|
| 299 | (when (null l) (return nil))
|
|---|
| 300 | (setq l (line-previous l)))))
|
|---|
| 301 | (do* ()
|
|---|
| 302 | ((null l))
|
|---|
| 303 | (setq chars (line-chars l) len (length chars))
|
|---|
| 304 | ;; If the end of this line is the first line in the pattern then check
|
|---|
| 305 | ;; to see if the other lines match.
|
|---|
| 306 | (when (and (>= len firstlen)
|
|---|
| 307 | (,test-fun chars first other
|
|---|
| 308 | :start1 (- len firstlen) :end1 len
|
|---|
| 309 | :end2 firstlen))
|
|---|
| 310 | (when
|
|---|
| 311 | (do ((m (line-next l) (line-next m))
|
|---|
| 312 | (i 2 (1+ i))
|
|---|
| 313 | (next (svref ,pattern 1) (svref ,pattern i))
|
|---|
| 314 | ,@(if pattern2
|
|---|
| 315 | `((another (svref ,pattern2 1)
|
|---|
| 316 | (svref ,pattern2 i))))
|
|---|
| 317 | (len 0)
|
|---|
| 318 | (nextlen 0)
|
|---|
| 319 | (chars ""))
|
|---|
| 320 | ((null m))
|
|---|
| 321 | (declare (simple-string next chars) (fixnum len nextlen i))
|
|---|
| 322 | (setq chars (line-chars m) nextlen (length next)
|
|---|
| 323 | len (length chars))
|
|---|
| 324 | ;; When on last line of pattern, check if prefix of line.
|
|---|
| 325 | (when (= i patlen)
|
|---|
| 326 | (return (and (>= len nextlen)
|
|---|
| 327 | (,test-fun chars next another :end1 nextlen
|
|---|
| 328 | :end2 nextlen))))
|
|---|
| 329 | (unless (,test-fun chars next another :end1 len
|
|---|
| 330 | :end2 nextlen)
|
|---|
| 331 | (return nil)))
|
|---|
| 332 | (setq ,line l ,start (- len firstlen))
|
|---|
| 333 | (return t)))
|
|---|
| 334 | ;; If not, try the next line
|
|---|
| 335 | (setq l ,(if forward-p '(line-next l) '(line-previous l))))))
|
|---|
| 336 | |
|---|
| 337 |
|
|---|
| 338 | ;;;; String-comparison macros that are passed to newline-search-macro
|
|---|
| 339 |
|
|---|
| 340 | ;;; case-sensitive-test-fun -- Internal
|
|---|
| 341 | ;;;
|
|---|
| 342 | ;;; Just thows away the extra arg and calls string=.
|
|---|
| 343 | ;;;
|
|---|
| 344 | (defmacro case-sensitive-test-fun (string1 string2 ignore &rest keys)
|
|---|
| 345 | (declare (ignore ignore))
|
|---|
| 346 | `(string= ,string1 ,string2 ,@keys))
|
|---|
| 347 |
|
|---|
| 348 | ;;; case-insensitive-test-fun -- Internal
|
|---|
| 349 | ;;;
|
|---|
| 350 | ;;; First compare the characters hashed with hashed-string2 and then
|
|---|
| 351 | ;;; only if they agree do an actual compare with case-folding.
|
|---|
| 352 | ;;;
|
|---|
| 353 | (defmacro case-insensitive-test-fun (string1 string2 hashed-string2
|
|---|
| 354 | &key end1 (start1 0) end2)
|
|---|
| 355 | `(when (= (- ,end1 ,start1) ,end2)
|
|---|
| 356 | (do ((i 0 (1+ i)))
|
|---|
| 357 | ((= i ,end2)
|
|---|
| 358 | (dotimes (i ,end2 t)
|
|---|
| 359 | (when (char/= (search-char-upcase (schar ,string1 (+ ,start1 i)))
|
|---|
| 360 | (schar ,string2 i))
|
|---|
| 361 | (return nil))))
|
|---|
| 362 | (when (/= (search-hash-code (schar ,string1 (+ ,start1 i)))
|
|---|
| 363 | (svref ,hashed-string2 i))
|
|---|
| 364 | (return nil)))))
|
|---|
| 365 | ); eval-when (:compile-toplevel :execute)
|
|---|
| 366 | |
|---|
| 367 |
|
|---|
| [12170] | 368 | ;;; compute-boyer-moore-jumps -- Internal
|
|---|
| [6] | 369 | ;;;
|
|---|
| 370 | ;;; Compute return a jump-vector to do a Boyer-Moore search for
|
|---|
| [12143] | 371 | ;;; the "string" of things in Vector. Access-fun is a function
|
|---|
| [6] | 372 | ;;; that aref's vector and returns a number.
|
|---|
| [13586] | 373 | ;;;
|
|---|
| [6] | 374 | (defun compute-boyer-moore-jumps (vec access-fun)
|
|---|
| 375 | (declare (simple-vector vec))
|
|---|
| 376 | (let ((jumps (new-search-vector vec access-fun))
|
|---|
| 377 | (len (length vec)))
|
|---|
| 378 | (declare (simple-vector jumps))
|
|---|
| [13586] | 379 | (when (zerop len) (editor-error "Zero length search string not allowed."))
|
|---|
| 380 | ;; The default jump is the length of the search string.
|
|---|
| [6] | 381 | (dotimes (i (length jumps))
|
|---|
| 382 | (setf (aref jumps i) len))
|
|---|
| 383 | ;; For chars in the string the jump is the distance from the end.
|
|---|
| 384 | (dotimes (i len)
|
|---|
| 385 | (setf (aref jumps (funcall access-fun vec i)) (- len i 1)))
|
|---|
| 386 | jumps))
|
|---|
| 387 |
|
|---|
| 388 |
|
|---|
| 389 | |
|---|
| 390 |
|
|---|
| 391 | ;;;; Case insensitive searches
|
|---|
| 392 |
|
|---|
| 393 | ;;; In order to avoid case folding, we do a case-insensitive hash of
|
|---|
| 394 | ;;; each character. We then search for string in this translated
|
|---|
| 395 | ;;; character set, and reject false successes by checking of the found
|
|---|
| 396 | ;;; string is string-equal the the original search string.
|
|---|
| 397 | ;;;
|
|---|
| 398 |
|
|---|
| 399 | (defstruct (string-insensitive-search-pattern
|
|---|
| 400 | (:include search-pattern)
|
|---|
| 401 | (:conc-name string-insensitive-)
|
|---|
| 402 | (:print-function %print-search-pattern))
|
|---|
| 403 | jumps
|
|---|
| 404 | hashed-string
|
|---|
| 405 | folded-string)
|
|---|
| 406 |
|
|---|
| 407 | ;;; Search-Hash-String -- Internal
|
|---|
| 408 | ;;;
|
|---|
| 409 | ;;; Return a simple-vector containing the search-hash-codes of the
|
|---|
| 410 | ;;; characters in String.
|
|---|
| 411 | ;;;
|
|---|
| 412 | (defun search-hash-string (string)
|
|---|
| 413 | (declare (simple-string string))
|
|---|
| 414 | (let* ((len (length string))
|
|---|
| 415 | (result (make-array len)))
|
|---|
| 416 | (declare (fixnum len) (simple-vector result))
|
|---|
| 417 | (dotimes (i len result)
|
|---|
| 418 | (setf (aref result i) (search-hash-code (schar string i))))))
|
|---|
| 419 |
|
|---|
| 420 | ;;; make-insensitive-newline-pattern -- Internal
|
|---|
| 421 | ;;;
|
|---|
| 422 | ;;; Make bash in fields in a string-insensitive-search-pattern to
|
|---|
| 423 | ;;; do a search for a string with newlines in it.
|
|---|
| 424 | ;;;
|
|---|
| 425 | (defun make-insensitive-newline-pattern (pattern folded-string)
|
|---|
| 426 | (declare (simple-string folded-string))
|
|---|
| 427 | (let* ((len (length folded-string))
|
|---|
| 428 | (num (1+ (count #\newline folded-string :end len)))
|
|---|
| 429 | (hashed (make-array num))
|
|---|
| 430 | (folded (make-array num)))
|
|---|
| 431 | (declare (simple-vector hashed folded) (fixnum len num))
|
|---|
| 432 | (do ((prev 0 nl)
|
|---|
| 433 | (i 0 (1+ i))
|
|---|
| 434 | (nl (position #\newline folded-string :end len)
|
|---|
| 435 | (position #\newline folded-string :start nl :end len)))
|
|---|
| 436 | ((null nl)
|
|---|
| 437 | (let ((piece (subseq folded-string prev len)))
|
|---|
| 438 | (setf (aref folded i) piece)
|
|---|
| 439 | (setf (aref hashed i) (search-hash-string piece))))
|
|---|
| 440 | (let ((piece (subseq folded-string prev nl)))
|
|---|
| 441 | (setf (aref folded i) piece)
|
|---|
| 442 | (setf (aref hashed i) (search-hash-string piece)))
|
|---|
| 443 | (incf nl))
|
|---|
| 444 | (setf (string-insensitive-folded-string pattern) folded
|
|---|
| 445 | (string-insensitive-hashed-string pattern) hashed)))
|
|---|
| 446 | |
|---|
| 447 |
|
|---|
| 448 |
|
|---|
| 449 | (define-search-kind :string-insensitive (direction pattern old)
|
|---|
| 450 | ":string-insensitive - Pattern is a string to do a case-insensitive
|
|---|
| 451 | search for."
|
|---|
| 452 | (unless old (setq old (make-string-insensitive-search-pattern)))
|
|---|
| 453 | (setf (search-pattern-kind old) :string-insensitive
|
|---|
| 454 | (search-pattern-direction old) direction
|
|---|
| 455 | (search-pattern-pattern old) pattern)
|
|---|
| 456 | (let* ((folded-string (string-upcase pattern)))
|
|---|
| 457 | (declare (simple-string folded-string))
|
|---|
| 458 | (cond
|
|---|
| 459 | ((find #\newline folded-string)
|
|---|
| 460 | (make-insensitive-newline-pattern old folded-string)
|
|---|
| 461 | (setf (search-pattern-search-function old)
|
|---|
| 462 | (if (eq direction :forward)
|
|---|
| 463 | #'insensitive-find-newline-once-forward-method
|
|---|
| [8774] | 464 | #'insensitive-find-newline-once-backward-method))
|
|---|
| [6] | 465 | (setf (search-pattern-reclaim-function old) #'identity))
|
|---|
| 466 | (t
|
|---|
| 467 | (case direction
|
|---|
| 468 | (:forward
|
|---|
| 469 | (setf (search-pattern-search-function old)
|
|---|
| 470 | #'insensitive-find-string-once-forward-method))
|
|---|
| 471 | (t
|
|---|
| 472 | (setf (search-pattern-search-function old)
|
|---|
| 473 | #'insensitive-find-string-once-backward-method)
|
|---|
| 474 | (setq folded-string (nreverse folded-string))))
|
|---|
| 475 | (let ((hashed-string (search-hash-string folded-string)))
|
|---|
| 476 | (setf (string-insensitive-hashed-string old) hashed-string
|
|---|
| 477 | (string-insensitive-folded-string old) folded-string)
|
|---|
| 478 | (setf (string-insensitive-jumps old)
|
|---|
| 479 | (compute-boyer-moore-jumps hashed-string #'svref))
|
|---|
| 480 | (setf (search-pattern-reclaim-function old)
|
|---|
| 481 | #'(lambda (p)
|
|---|
| 482 | (dispose-search-vector (string-insensitive-jumps p))))))))
|
|---|
| 483 | old)
|
|---|
| 484 | |
|---|
| 485 |
|
|---|
| 486 | (defun insensitive-find-string-once-forward-method (pattern line start)
|
|---|
| 487 | (let* ((hashed-string (string-insensitive-hashed-string pattern))
|
|---|
| 488 | (folded-string (string-insensitive-folded-string pattern))
|
|---|
| 489 | (jumps (string-insensitive-jumps pattern))
|
|---|
| 490 | (patlen (length hashed-string))
|
|---|
| 491 | (last (1- patlen)))
|
|---|
| 492 | (declare (simple-vector jumps hashed-string) (simple-string folded-string)
|
|---|
| 493 | (fixnum patlen last))
|
|---|
| 494 | (when (search-once-forward-macro
|
|---|
| 495 | line start insensitive-string-search-macro
|
|---|
| 496 | hashed-string folded-string patlen last jumps + -)
|
|---|
| 497 | (values line start patlen))))
|
|---|
| 498 |
|
|---|
| 499 | (defun insensitive-find-string-once-backward-method (pattern line start)
|
|---|
| 500 | (let* ((hashed-string (string-insensitive-hashed-string pattern))
|
|---|
| 501 | (folded-string (string-insensitive-folded-string pattern))
|
|---|
| 502 | (jumps (string-insensitive-jumps pattern))
|
|---|
| 503 | (patlen (length hashed-string))
|
|---|
| 504 | (last (1- patlen)))
|
|---|
| 505 | (declare (simple-vector jumps hashed-string) (simple-string folded-string)
|
|---|
| 506 | (fixnum patlen last))
|
|---|
| 507 | (when (search-once-backward-macro
|
|---|
| 508 | line start insensitive-string-search-macro
|
|---|
| 509 | nil hashed-string folded-string patlen last jumps - +)
|
|---|
| 510 | (values line (- start last) patlen))))
|
|---|
| 511 |
|
|---|
| 512 | (eval-when (:compile-toplevel :execute)
|
|---|
| 513 | (defmacro def-insensitive-newline-search-method (name direction)
|
|---|
| 514 | `(defun ,name (pattern line start)
|
|---|
| 515 | (let* ((hashed (string-insensitive-hashed-string pattern))
|
|---|
| 516 | (folded-string (string-insensitive-folded-string pattern))
|
|---|
| 517 | (patlen (length (the string (search-pattern-pattern pattern)))))
|
|---|
| 518 | (declare (simple-vector hashed folded-string))
|
|---|
| 519 | (when (newline-search-macro line start case-insensitive-test-fun
|
|---|
| 520 | folded-string ,direction hashed)
|
|---|
| 521 | (values line start patlen)))))
|
|---|
| 522 | ); eval-when (:compile-toplevel :execute)
|
|---|
| 523 |
|
|---|
| 524 | (def-insensitive-newline-search-method
|
|---|
| 525 | insensitive-find-newline-once-forward-method t)
|
|---|
| 526 | (def-insensitive-newline-search-method
|
|---|
| 527 | insensitive-find-newline-once-backward-method nil)
|
|---|
| 528 | |
|---|
| 529 |
|
|---|
| 530 | ;;;; And Snore, case sensitive.
|
|---|
| 531 | ;;;
|
|---|
| 532 | ;;; This is horribly repetitive, but if I introduce another level of
|
|---|
| 533 | ;;; macroexpansion I will go Insaaaane....
|
|---|
| 534 | ;;;
|
|---|
| 535 | (defstruct (string-sensitive-search-pattern
|
|---|
| 536 | (:include search-pattern)
|
|---|
| 537 | (:conc-name string-sensitive-)
|
|---|
| 538 | (:print-function %print-search-pattern))
|
|---|
| 539 | string
|
|---|
| 540 | jumps)
|
|---|
| 541 |
|
|---|
| 542 | ;;; make-sensitive-newline-pattern -- Internal
|
|---|
| 543 | ;;;
|
|---|
| 544 | ;;; The same, only more sensitive (it hurts when you do that...)
|
|---|
| 545 | ;;;
|
|---|
| 546 | (defun make-sensitive-newline-pattern (pattern string)
|
|---|
| 547 | (declare (simple-vector string))
|
|---|
| 548 | (let* ((string (coerce string 'simple-string))
|
|---|
| 549 | (len (length string))
|
|---|
| 550 | (num (1+ (count #\newline string :end len)))
|
|---|
| 551 | (sliced (make-array num)))
|
|---|
| 552 | (declare (simple-string string) (simple-vector sliced) (fixnum len num))
|
|---|
| 553 | (do ((prev 0 nl)
|
|---|
| 554 | (i 0 (1+ i))
|
|---|
| 555 | (nl (position #\newline string :end len)
|
|---|
| 556 | (position #\newline string :start nl :end len)))
|
|---|
| 557 | ((null nl)
|
|---|
| 558 | (setf (aref sliced i) (subseq string prev len)))
|
|---|
| 559 | (setf (aref sliced i) (subseq string prev nl))
|
|---|
| 560 | (incf nl))
|
|---|
| 561 | (setf (string-sensitive-string pattern) sliced)))
|
|---|
| 562 | |
|---|
| 563 |
|
|---|
| 564 |
|
|---|
| 565 | (define-search-kind :string-sensitive (direction pattern old)
|
|---|
| 566 | ":string-sensitive - Pattern is a string to do a case-sensitive
|
|---|
| 567 | search for."
|
|---|
| 568 | (unless old (setq old (make-string-sensitive-search-pattern)))
|
|---|
| 569 | (setf (search-pattern-kind old) :string-sensitive
|
|---|
| 570 | (search-pattern-direction old) direction
|
|---|
| 571 | (search-pattern-pattern old) pattern)
|
|---|
| 572 | (let* ((string (coerce pattern 'simple-vector)))
|
|---|
| 573 | (declare (simple-vector string))
|
|---|
| 574 | (cond
|
|---|
| 575 | ((find #\newline string)
|
|---|
| 576 | (make-sensitive-newline-pattern old string)
|
|---|
| [8774] | 577 | (setf (search-pattern-search-function old)
|
|---|
| [6] | 578 | (if (eq direction :forward)
|
|---|
| 579 | #'sensitive-find-newline-once-forward-method
|
|---|
| 580 | #'sensitive-find-newline-once-backward-method))
|
|---|
| 581 | (setf (search-pattern-reclaim-function old) #'identity))
|
|---|
| 582 | (t
|
|---|
| 583 | (case direction
|
|---|
| 584 | (:forward
|
|---|
| 585 | (setf (search-pattern-search-function old)
|
|---|
| 586 | #'sensitive-find-string-once-forward-method))
|
|---|
| 587 | (t
|
|---|
| 588 | (setf (search-pattern-search-function old)
|
|---|
| 589 | #'sensitive-find-string-once-backward-method)
|
|---|
| 590 | (setq string (nreverse string))))
|
|---|
| 591 | (setf (string-sensitive-string old) string)
|
|---|
| 592 | (setf (string-sensitive-jumps old)
|
|---|
| 593 | (compute-boyer-moore-jumps
|
|---|
| 594 | string #'(lambda (v i) (char-code (svref v i)))))
|
|---|
| 595 | (setf (search-pattern-reclaim-function old)
|
|---|
| 596 | #'(lambda (p)
|
|---|
| 597 | (dispose-search-vector (string-sensitive-jumps p)))))))
|
|---|
| 598 | old)
|
|---|
| 599 |
|
|---|
| 600 | |
|---|
| 601 |
|
|---|
| 602 | (defun sensitive-find-string-once-forward-method (pattern line start)
|
|---|
| 603 | (let* ((string (string-sensitive-string pattern))
|
|---|
| 604 | (jumps (string-sensitive-jumps pattern))
|
|---|
| 605 | (patlen (length string))
|
|---|
| 606 | (last (1- patlen)))
|
|---|
| 607 | (declare (simple-vector jumps string) (fixnum patlen last))
|
|---|
| 608 | (when (search-once-forward-macro
|
|---|
| 609 | line start sensitive-string-search-macro
|
|---|
| 610 | string patlen last jumps + -)
|
|---|
| 611 | (values line start patlen))))
|
|---|
| 612 |
|
|---|
| 613 | (defun sensitive-find-string-once-backward-method (pattern line start)
|
|---|
| 614 | (let* ((string (string-sensitive-string pattern))
|
|---|
| 615 | (jumps (string-sensitive-jumps pattern))
|
|---|
| 616 | (patlen (length string))
|
|---|
| 617 | (last (1- patlen)))
|
|---|
| 618 | (declare (simple-vector jumps string) (fixnum patlen last))
|
|---|
| 619 | (when (search-once-backward-macro
|
|---|
| 620 | line start sensitive-string-search-macro
|
|---|
| 621 | nil string patlen last jumps - +)
|
|---|
| 622 | (values line (- start last) patlen))))
|
|---|
| 623 |
|
|---|
| 624 | (eval-when (:compile-toplevel :execute)
|
|---|
| 625 | (defmacro def-sensitive-newline-search-method (name direction)
|
|---|
| [8428] | 626 | `(defun ,name (pattern line start)
|
|---|
| [6] | 627 | (let* ((string (string-sensitive-string pattern))
|
|---|
| 628 | (patlen (length (the string (search-pattern-pattern pattern)))))
|
|---|
| 629 | (declare (simple-vector string))
|
|---|
| [8428] | 630 | (when (newline-search-macro line start case-sensitive-test-fun
|
|---|
| 631 | string ,direction)
|
|---|
| [12635] | 632 | (values line start patlen)))))
|
|---|
| 633 | ); eval-when (:compile-toplevel :execute)
|
|---|
| [6582] | 634 |
|
|---|
| [6] | 635 | (def-sensitive-newline-search-method
|
|---|
| 636 | sensitive-find-newline-once-forward-method t)
|
|---|
| 637 | (def-sensitive-newline-search-method
|
|---|
| 638 | sensitive-find-newline-once-backward-method nil)
|
|---|
| [8428] | 639 | |
|---|
| 640 |
|
|---|
| [12635] | 641 | (defun find-pattern (mark search-pattern &optional stop-mark)
|
|---|
| 642 | "Find a match of Search-Pattern starting at Mark. Mark is moved to
|
|---|
| 643 | point before the match and the number of characters matched is returned.
|
|---|
| 644 | If there is no match for the pattern then Mark is not modified and NIL
|
|---|
| 645 | is returned.
|
|---|
| 646 | If stop-mark is specified, NIL is returned and mark is not moved if
|
|---|
| 647 | the point before the match is after stop-mark for forward search or
|
|---|
| [6] | 648 | before stop-mark for backward search"
|
|---|
| 649 | (close-line)
|
|---|
| 650 | (multiple-value-bind (line start matched)
|
|---|
| 651 | (funcall (search-pattern-search-function search-pattern)
|
|---|
| 652 | search-pattern (mark-line mark)
|
|---|
| 653 | (mark-charpos mark))
|
|---|
| 654 | (when (and matched
|
|---|
| 655 | (or (null stop-mark)
|
|---|
| 656 | (if (eq (search-pattern-direction search-pattern) :forward)
|
|---|
| 657 | (or (< (line-number line) (line-number (mark-line stop-mark)))
|
|---|
| [6582] | 658 | (and (eq line (mark-line stop-mark))
|
|---|
| 659 | (<= start (mark-charpos stop-mark))))
|
|---|
| 660 | (or (< (line-number (mark-line stop-mark)) (line-number line))
|
|---|
| 661 | (and (eq (mark-line stop-mark) line)
|
|---|
| 662 | (<= (mark-charpos stop-mark) start))))))
|
|---|
| 663 | (move-to-position mark start line)
|
|---|
| 664 | matched)))
|
|---|
| 665 |
|
|---|
| 666 | ;;; replace-pattern -- Public
|
|---|
| 667 | ;;;
|
|---|
| 668 | ;;;
|
|---|
| 669 | (defun replace-pattern (mark search-pattern replacement &optional n)
|
|---|
| 670 | "Replaces N occurrences of the Search-Pattern with the Replacement string
|
|---|
| 671 | in the text starting at the given Mark. If N is Nil, all occurrences
|
|---|
| 672 | following the Mark are replaced."
|
|---|
| 673 | (close-line)
|
|---|
| 674 | (do* ((replacement (coerce replacement 'simple-string))
|
|---|
| 675 | (new (length (the simple-string replacement)))
|
|---|
| 676 | (fun (search-pattern-search-function search-pattern))
|
|---|
| 677 | (forward-p (eq (search-pattern-direction search-pattern) :forward))
|
|---|
| 678 | (n (if n (1- n) -1) (1- n))
|
|---|
| 679 | (m (copy-mark mark :temporary)) line start matched)
|
|---|
| 680 | (())
|
|---|
| 681 | (multiple-value-setq (line start matched)
|
|---|
| 682 | (funcall fun search-pattern (mark-line m) (mark-charpos m)))
|
|---|
| 683 | (unless matched (return m))
|
|---|
| 684 | (setf (mark-line m) line (mark-charpos m) start)
|
|---|
| 685 | (delete-characters m matched)
|
|---|
| 686 | (insert-string m replacement)
|
|---|
| 687 | (when forward-p (character-offset m new))
|
|---|
| 688 | (when (zerop n) (return m))
|
|---|
| 689 | (close-line)))
|
|---|