blob: a75d0ee7bb3c6ec5b8d1f489db4ac5b54843ad7b [file] [log] [blame]
Bram Moolenaare2db6c92015-06-19 18:48:41 +02001" LogiPat:
2" Author: Charles E. Campbell
3" Date: Mar 13, 2013
4" Version: 3
5" Purpose: to do Boolean-logic based regular expression pattern matching
6" Copyright: Copyright (C) 1999-2011 Charles E. Campbell {{{1
7" Permission is hereby granted to use and distribute this code,
8" with or without modifications, provided that this copyright
9" notice is copied with it. Like most anything else that's free,
10" LogiPat.vim is provided *as is* and comes with no warranty
11" of any kind, either expressed or implied. By using this
12" plugin, you agree that in no event will the copyright
13" holder be liable for any damages resulting from the use
14" of this software.
15"
16" Usage: {{{1
17" :LogiPat ...
18"
19" Boolean logic supported:
20" () grouping operators
21" ! not the following pattern
22" | logical or
23" & logical and
24" "..pattern.."
25" Example: {{{1
26" :LogiPat !("january"|"february")
27" would match all strings not containing the strings january
28" or february
29" GetLatestVimScripts: 1290 1 :AutoInstall: LogiPat.vim
30"
31" Behold, you will conceive in your womb, and bring forth a son, {{{1
32" and will call his name Jesus. He will be great, and will be
33" called the Son of the Most High. The Lord God will give him the
34" throne of his father, David, and he will reign over the house of
35" Jacob forever. There will be no end to his kingdom. (Luke 1:31-33 WEB)
36
37" ---------------------------------------------------------------------
38" Load Once: {{{1
39if &cp || exists("loaded_logipat")
40 finish
41endif
42let g:loaded_LogiPat = "v3"
43let s:keepcpo = &cpo
44set cpo&vim
45"DechoRemOn
46
47" ---------------------------------------------------------------------
48" Public Interface: {{{1
49com! -nargs=* LogiPat call LogiPat(<q-args>,1)
50silent! com -nargs=* LP call LogiPat(<q-args>,1)
51com! -nargs=+ ELP echomsg LogiPat(<q-args>)
52com! -nargs=+ LogiPatFlags let s:LogiPatFlags="<args>"
53silent! com -nargs=+ LPF let s:LogiPatFlags="<args>"
54
55" =====================================================================
56" Functions: {{{1
57
58" ---------------------------------------------------------------------
59" LogiPat: this function interprets the boolean-logic pattern {{{2
60fun! LogiPat(pat,...)
61" call Dfunc("LogiPat(pat<".a:pat.">)")
62
63 " LogiPat(pat,dosearch)
64 if a:0 > 0
65 let dosearch= a:1
66 else
67 let dosearch= 0
68 endif
69
70 let s:npatstack = 0
71 let s:nopstack = 0
72 let s:preclvl = 0
73 let expr = a:pat
74
75 " Lexer/Parser
76 while expr != ""
77" call Decho("expr<".expr.">")
78
79 if expr =~ '^"'
80 " push a Pattern; accept "" as a single " in the pattern
81 let expr = substitute(expr,'^\s*"','','')
82 let pat = substitute(expr,'^\(\%([^"]\|\"\"\)\{-}\)"\([^"].*$\|$\)','\1','')
83 let pat = substitute(pat,'""','"','g')
84 let expr = substitute(expr,'^\(\%([^"]\|\"\"\)\{-}\)"\([^"].*$\|$\)','\2','')
85 let expr = substitute(expr,'^\s*','','')
86" call Decho("pat<".pat."> expr<".expr.">")
87
88 call s:LP_PatPush('.*'.pat.'.*')
89
90 elseif expr =~ '^[!()|&]'
91 " push an operator
92 let op = strpart(expr,0,1)
93 let expr = strpart(expr,strlen(op))
94 " allow for those who can't resist doubling their and/or operators
95 if op =~ '[|&]' && expr[0] == op
96 let expr = strpart(expr,strlen(op))
97 endif
98 call s:LP_OpPush(op)
99
100 elseif expr =~ '^\s'
101 " skip whitespace
102 let expr= strpart(expr,1)
103
104 else
105 echoerr "operator<".strpart(expr,0,1)."> not supported (yet)"
106 let expr= strpart(expr,1)
107 endif
108
109 endwhile
110
111 " Final Execution
112 call s:LP_OpPush('Z')
113
114 let result= s:LP_PatPop(1)
115" call Decho("result=".result)
116
117 " sanity checks and cleanup
118 if s:npatstack > 0
119 echoerr s:npatstack." patterns left on stack!"
120 let s:npatstack= 0
121 endif
122 if s:nopstack > 0
123 echoerr s:nopstack." operators left on stack!"
124 let s:nopstack= 0
125 endif
126
127 " perform the indicated search
128 if dosearch
129 if exists("s:LogiPatFlags")
130" call Decho("search(result<".result."> LogiPatFlags<".s:LogiPatFlags.">)")
131 call search(result,s:LogiPatFlags)
132 else
133" call Decho("search(result<".result.">)")
134 call search(result)
135 endif
136 let @/= result
137 endif
138
139" call Dret("LogiPat ".result)
140 return result
141endfun
142
143" ---------------------------------------------------------------------
144" s:String: Vim6.4 doesn't have string() {{{2
145func! s:String(str)
146 return "'".escape(a:str, '"')."'"
147endfunc
148
149" ---------------------------------------------------------------------
150" LP_PatPush: {{{2
151fun! s:LP_PatPush(pat)
152" call Dfunc("LP_PatPush(pat<".a:pat.">)")
153 let s:npatstack = s:npatstack + 1
154 let s:patstack_{s:npatstack} = a:pat
155" call s:StackLook("patpush") "Decho
156" call Dret("LP_PatPush : npatstack=".s:npatstack)
157endfun
158
159" ---------------------------------------------------------------------
160" LP_PatPop: pop a number/variable from LogiPat's pattern stack {{{2
161fun! s:LP_PatPop(lookup)
162" call Dfunc("LP_PatPop(lookup=".a:lookup.")")
163 if s:npatstack > 0
164 let ret = s:patstack_{s:npatstack}
165 let s:npatstack = s:npatstack - 1
166 else
167 let ret= "---error---"
168 echoerr "(LogiPat) invalid expression"
169 endif
170" call s:StackLook("patpop") "Decho
171" call Dret("LP_PatPop ".ret)
172 return ret
173endfun
174
175" ---------------------------------------------------------------------
176" LP_OpPush: {{{2
177fun! s:LP_OpPush(op)
178" call Dfunc("LP_OpPush(op<".a:op.">)")
179
180 " determine new operator's precedence level
181 if a:op == '('
182 let s:preclvl= s:preclvl + 10
183 let preclvl = s:preclvl
184 elseif a:op == ')'
185 let s:preclvl= s:preclvl - 10
186 if s:preclvl < 0
187 let s:preclvl= 0
188 echoerr "too many )s"
189 endif
190 let preclvl= s:preclvl
191 elseif a:op =~ '|'
192 let preclvl= s:preclvl + 2
193 elseif a:op =~ '&'
194 let preclvl= s:preclvl + 4
195 elseif a:op == '!'
196 let preclvl= s:preclvl + 6
197 elseif a:op == 'Z'
198 let preclvl= -1
199 else
200 echoerr "expr<".expr."> not supported (yet)"
201 let preclvl= s:preclvl
202 endif
203" call Decho("new operator<".a:op."> preclvl=".preclvl)
204
205 " execute higher-precdence operators
206" call Decho("execute higher-precedence operators")
207 call s:LP_Execute(preclvl)
208
209 " push new operator onto operator-stack
210" call Decho("push new operator<".a:op."> onto stack with preclvl=".preclvl." at nopstack=".(s:nopstack+1))
211 if a:op =~ '!'
212 let s:nopstack = s:nopstack + 1
213 let s:opprec_{s:nopstack} = preclvl
214 let s:opstack_{s:nopstack} = a:op
215 elseif a:op =~ '|'
216 let s:nopstack = s:nopstack + 1
217 let s:opprec_{s:nopstack} = preclvl
218 let s:opstack_{s:nopstack} = a:op
219 elseif a:op == '&'
220 let s:nopstack = s:nopstack + 1
221 let s:opprec_{s:nopstack} = preclvl
222 let s:opstack_{s:nopstack} = a:op
223 endif
224
225" call s:StackLook("oppush") "Decho
226" call Dret("LP_OpPush : s:preclvl=".s:preclvl)
227endfun
228
229" ---------------------------------------------------------------------
230" LP_Execute: execute operators from opstack using pattern stack {{{2
231fun! s:LP_Execute(preclvl)
232" call Dfunc("LP_Execute(preclvl=".a:preclvl.") npatstack=".s:npatstack." nopstack=".s:nopstack)
233
234 " execute all higher precedence operators
235 while s:nopstack > 0 && a:preclvl < s:opprec_{s:nopstack}
236 let op= s:opstack_{s:nopstack}
237" call Decho("op<".op."> nop=".s:nopstack." [preclvl=".a:preclvl."] < [opprec_".s:nopstack."=".s:opprec_{s:nopstack}."]")
238
239 let s:nopstack = s:nopstack - 1
240
241 if op == '!'
242 let n1= s:LP_PatPop(1)
243 call s:LP_PatPush(s:LP_Not(n1))
244
245 elseif op == '|'
246 let n1= s:LP_PatPop(1)
247 let n2= s:LP_PatPop(1)
248 call s:LP_PatPush(s:LP_Or(n2,n1))
249
250 elseif op =~ '&'
251 let n1= s:LP_PatPop(1)
252 let n2= s:LP_PatPop(1)
253 call s:LP_PatPush(s:LP_And(n2,n1))
254 endif
255
256" call s:StackLook("execute") "Decho
257 endwhile
258
259" call Dret("LP_Execute")
260endfun
261
262" ---------------------------------------------------------------------
263" LP_Not: writes a logical-not for a pattern {{{2
264fun! s:LP_Not(pat)
265" call Dfunc("LP_Not(pat<".a:pat.">)")
266 if a:pat =~ '^\.\*' && a:pat =~ '\.\*$'
267 let pat= substitute(a:pat,'^\.\*\(.*\)\.\*$','\1','')
268 let ret= '^\%(\%('.pat.'\)\@!.\)*$'
269 else
270 let ret= '^\%(\%('.a:pat.'\)\@!.\)*$'
271 endif
272" call Dret("LP_Not ".ret)
273 return ret
274endfun
275
276" ---------------------------------------------------------------------
277" LP_Or: writes a logical-or branch using two patterns {{{2
278fun! s:LP_Or(pat1,pat2)
279" call Dfunc("LP_Or(pat1<".a:pat1."> pat2<".a:pat2.">)")
280 let ret= '\%('.a:pat1.'\|'.a:pat2.'\)'
281" call Dret("LP_Or ".ret)
282 return ret
283endfun
284
285" ---------------------------------------------------------------------
286" LP_And: writes a logical-and concat using two patterns {{{2
287fun! s:LP_And(pat1,pat2)
288" call Dfunc("LP_And(pat1<".a:pat1."> pat2<".a:pat2.">)")
289 let ret= '\%('.a:pat1.'\&'.a:pat2.'\)'
290" call Dret("LP_And ".ret)
291 return ret
292endfun
293
294" ---------------------------------------------------------------------
295" StackLook: {{{2
296fun! s:StackLook(description)
297" call Dfunc("StackLook(description<".a:description.">)")
298 let iop = 1
299 let ifp = 1
300" call Decho("Pattern Operator")
301
302 " print both pattern and operator
303 while ifp <= s:npatstack && iop <= s:nopstack
304 let fp = s:patstack_{ifp}
305 let op = s:opstack_{iop}." (P".s:opprec_{s:nopstack}.')'
306 let fplen= strlen(fp)
307 if fplen < 30
308 let fp= fp.strpart(" ",1,30-fplen)
309 endif
310" call Decho(fp.op)
311 let ifp = ifp + 1
312 let iop = iop + 1
313 endwhile
314
315 " print just pattern
316 while ifp <= s:npatstack
317 let fp = s:patstack_{ifp}
318" call Decho(fp)
319 let ifp = ifp + 1
320 endwhile
321
322 " print just operator
323 while iop <= s:nopstack
324 let op = s:opstack_{iop}." (P".s:opprec_{s:nopstack}.')'
325" call Decho(" ".op)
326 let iop = iop + 1
327 endwhile
328" call Dret("StackLook")
329endfun
330
331" ---------------------------------------------------------------------
332" Cleanup And Modeline: {{{1
333let &cpo= s:keepcpo
334unlet s:keepcpo
335" vim: ts=4 fdm=marker