xref: /vim-8.2.3635/runtime/plugin/logiPat.vim (revision 818c9e7e)
1" 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
336