xref: /vim-8.2.3635/runtime/plugin/logiPat.vim (revision 939a1abe)
1" LogiPat: Boolean logical pattern matcher
2"   Author:  Charles E. Campbell
3"   Date:    Apr 04, 2016
4"   Version: 4
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 = "v4"
43let s:keepcpo        = &cpo
44set cpo&vim
45"DechoRemOn
46
47" ---------------------------------------------------------------------
48" Public Interface: {{{1
49com!     -nargs=* LogiPat		call   LogiPat(<q-args>,1)
50sil! com -nargs=* LP			call   LogiPat(<q-args>,1)
51sil! com -nargs=* LPR			call   LogiPat(<q-args>,1,"r")
52com!     -nargs=+ LPE			echomsg LogiPat(<q-args>)
53com!     -nargs=+ LogiPatFlags	let  s:LogiPatFlags="<args>"
54sil! com -nargs=+ LPF			let  s:LogiPatFlags="<args>"
55
56" =====================================================================
57" Functions: {{{1
58
59" ---------------------------------------------------------------------
60" LogiPat: this function interprets the boolean-logic pattern {{{2
61fun! LogiPat(pat,...)
62"  call Dfunc("LogiPat(pat<".a:pat.">)")
63
64  " LogiPat(pat,dosearch)
65  if a:0 > 0
66   let dosearch= a:1
67  else
68   let dosearch= 0
69  endif
70  if a:0 >= 3
71   let s:LogiPatFlags= a:3
72  endif
73
74  let s:npatstack = 0
75  let s:nopstack  = 0
76  let s:preclvl   = 0
77  let expr        = a:pat
78
79  " Lexer/Parser
80  while expr != ""
81"   call Decho("expr<".expr.">")
82
83   if expr =~ '^"'
84	" push a Pattern; accept "" as a single " in the pattern
85    let expr = substitute(expr,'^\s*"','','')
86    let pat  = substitute(expr,'^\(\%([^"]\|\"\"\)\{-}\)"\([^"].*$\|$\)','\1','')
87	let pat  = substitute(pat,'""','"','g')
88    let expr = substitute(expr,'^\(\%([^"]\|\"\"\)\{-}\)"\([^"].*$\|$\)','\2','')
89    let expr = substitute(expr,'^\s*','','')
90"    call Decho("pat<".pat."> expr<".expr.">")
91
92    call s:LP_PatPush('.*'.pat.'.*')
93
94   elseif expr =~ '^[!()|&]'
95    " push an operator
96    let op   = strpart(expr,0,1)
97    let expr = strpart(expr,strlen(op))
98	" allow for those who can't resist doubling their and/or operators
99	if op =~ '[|&]' && expr[0] == op
100     let expr = strpart(expr,strlen(op))
101	endif
102    call s:LP_OpPush(op)
103
104   elseif expr =~ '^\s'
105    " skip whitespace
106    let expr= strpart(expr,1)
107
108   else
109    echoerr "operator<".strpart(expr,0,1)."> not supported (yet)"
110    let expr= strpart(expr,1)
111   endif
112
113  endwhile
114
115  " Final Execution
116  call s:LP_OpPush('Z')
117
118  let result= s:LP_PatPop(1)
119"  call Decho("result=".result)
120
121  " sanity checks and cleanup
122  if s:npatstack > 0
123   echoerr s:npatstack." patterns left on stack!"
124   let s:npatstack= 0
125  endif
126  if s:nopstack > 0
127   echoerr s:nopstack." operators left on stack!"
128   let s:nopstack= 0
129  endif
130
131  " perform the indicated search
132  if dosearch
133   if exists("s:LogiPatFlags") && s:LogiPatFlags != ""
134"  call Decho("search(result<".result."> LogiPatFlags<".s:LogiPatFlags.">)")
135    call search(result,s:LogiPatFlags)
136   else
137"  call Decho("search(result<".result.">)")
138    call search(result)
139   endif
140   let @/= result
141  endif
142
143"  call Dret("LogiPat ".result)
144  return result
145endfun
146
147" ---------------------------------------------------------------------
148" s:String: Vim6.4 doesn't have string() {{{2
149func! s:String(str)
150  return "'".escape(a:str, '"')."'"
151endfunc
152
153" ---------------------------------------------------------------------
154" LP_PatPush: {{{2
155fun! s:LP_PatPush(pat)
156"  call Dfunc("LP_PatPush(pat<".a:pat.">)")
157  let s:npatstack              = s:npatstack + 1
158  let s:patstack_{s:npatstack} = a:pat
159"  call s:StackLook("patpush") "Decho
160"  call Dret("LP_PatPush : npatstack=".s:npatstack)
161endfun
162
163" ---------------------------------------------------------------------
164" LP_PatPop: pop a number/variable from LogiPat's pattern stack {{{2
165fun! s:LP_PatPop(lookup)
166"  call Dfunc("LP_PatPop(lookup=".a:lookup.")")
167  if s:npatstack > 0
168   let ret         = s:patstack_{s:npatstack}
169   let s:npatstack = s:npatstack - 1
170  else
171   let ret= "---error---"
172   echoerr "(LogiPat) invalid expression"
173  endif
174"  call s:StackLook("patpop") "Decho
175"  call Dret("LP_PatPop ".ret)
176  return ret
177endfun
178
179" ---------------------------------------------------------------------
180" LP_OpPush: {{{2
181fun! s:LP_OpPush(op)
182"  call Dfunc("LP_OpPush(op<".a:op.">)")
183
184  " determine new operator's precedence level
185  if a:op == '('
186  	let s:preclvl= s:preclvl + 10
187	let preclvl  = s:preclvl
188  elseif a:op == ')'
189  	let s:preclvl= s:preclvl - 10
190   if s:preclvl < 0
191    let s:preclvl= 0
192    echoerr "too many )s"
193   endif
194   let preclvl= s:preclvl
195  elseif a:op =~ '|'
196   let preclvl= s:preclvl + 2
197  elseif a:op =~ '&'
198   let preclvl= s:preclvl + 4
199  elseif a:op == '!'
200   let preclvl= s:preclvl + 6
201  elseif a:op == 'Z'
202   let preclvl= -1
203  else
204   echoerr "expr<".expr."> not supported (yet)"
205   let preclvl= s:preclvl
206  endif
207"  call Decho("new operator<".a:op."> preclvl=".preclvl)
208
209  " execute higher-precdence operators
210"  call Decho("execute higher-precedence operators")
211  call s:LP_Execute(preclvl)
212
213  " push new operator onto operator-stack
214"  call Decho("push new operator<".a:op."> onto stack with preclvl=".preclvl." at nopstack=".(s:nopstack+1))
215  if 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  elseif a:op == '&'
224   let s:nopstack             = s:nopstack + 1
225   let s:opprec_{s:nopstack}  = preclvl
226   let s:opstack_{s:nopstack} = a:op
227  endif
228
229"  call s:StackLook("oppush") "Decho
230"  call Dret("LP_OpPush : s:preclvl=".s:preclvl)
231endfun
232
233" ---------------------------------------------------------------------
234" LP_Execute: execute operators from opstack using pattern stack {{{2
235fun! s:LP_Execute(preclvl)
236"  call Dfunc("LP_Execute(preclvl=".a:preclvl.") npatstack=".s:npatstack." nopstack=".s:nopstack)
237
238  " execute all higher precedence operators
239  while s:nopstack > 0 && a:preclvl < s:opprec_{s:nopstack}
240   let op= s:opstack_{s:nopstack}
241"   call Decho("op<".op."> nop=".s:nopstack." [preclvl=".a:preclvl."] < [opprec_".s:nopstack."=".s:opprec_{s:nopstack}."]")
242
243   let s:nopstack = s:nopstack - 1
244
245   if     op == '!'
246    let n1= s:LP_PatPop(1)
247	call s:LP_PatPush(s:LP_Not(n1))
248
249   elseif op == '|'
250    let n1= s:LP_PatPop(1)
251    let n2= s:LP_PatPop(1)
252    call s:LP_PatPush(s:LP_Or(n2,n1))
253
254   elseif op =~ '&'
255    let n1= s:LP_PatPop(1)
256    let n2= s:LP_PatPop(1)
257    call s:LP_PatPush(s:LP_And(n2,n1))
258   endif
259
260"   call s:StackLook("execute") "Decho
261  endwhile
262
263"  call Dret("LP_Execute")
264endfun
265
266" ---------------------------------------------------------------------
267" LP_Not: writes a logical-not for a pattern {{{2
268fun! s:LP_Not(pat)
269"  call Dfunc("LP_Not(pat<".a:pat.">)")
270  if a:pat =~ '^\.\*' && a:pat =~ '\.\*$'
271   let pat= substitute(a:pat,'^\.\*\(.*\)\.\*$','\1','')
272   let ret= '^\%(\%('.pat.'\)\@!.\)*$'
273  else
274   let ret= '^\%(\%('.a:pat.'\)\@!.\)*$'
275  endif
276"  call Dret("LP_Not ".ret)
277  return ret
278endfun
279
280" ---------------------------------------------------------------------
281" LP_Or: writes a logical-or branch using two patterns {{{2
282fun! s:LP_Or(pat1,pat2)
283"  call Dfunc("LP_Or(pat1<".a:pat1."> pat2<".a:pat2.">)")
284  let ret= '\%('.a:pat1.'\|'.a:pat2.'\)'
285"  call Dret("LP_Or ".ret)
286  return ret
287endfun
288
289" ---------------------------------------------------------------------
290" LP_And: writes a logical-and concat using two patterns {{{2
291fun! s:LP_And(pat1,pat2)
292"  call Dfunc("LP_And(pat1<".a:pat1."> pat2<".a:pat2.">)")
293  let ret= '\%('.a:pat1.'\&'.a:pat2.'\)'
294"  call Dret("LP_And ".ret)
295  return ret
296endfun
297
298" ---------------------------------------------------------------------
299" StackLook: {{{2
300fun! s:StackLook(description)
301"  call Dfunc("StackLook(description<".a:description.">)")
302  let iop = 1
303  let ifp = 1
304"  call Decho("Pattern                       Operator")
305
306  " print both pattern and operator
307  while ifp <= s:npatstack && iop <= s:nopstack
308   let fp = s:patstack_{ifp}
309   let op = s:opstack_{iop}." (P".s:opprec_{s:nopstack}.')'
310   let fplen= strlen(fp)
311   if fplen < 30
312   	let fp= fp.strpart("                              ",1,30-fplen)
313   endif
314"   call Decho(fp.op)
315   let ifp = ifp + 1
316   let iop = iop + 1
317  endwhile
318
319  " print just pattern
320  while ifp <= s:npatstack
321   let fp  = s:patstack_{ifp}
322"   call Decho(fp)
323   let ifp = ifp + 1
324  endwhile
325
326  " print just operator
327  while iop <= s:nopstack
328   let op  = s:opstack_{iop}." (P".s:opprec_{s:nopstack}.')'
329"   call Decho("                              ".op)
330   let iop = iop + 1
331  endwhile
332"  call Dret("StackLook")
333endfun
334
335" ---------------------------------------------------------------------
336"  Cleanup And Modeline: {{{1
337let &cpo= s:keepcpo
338unlet s:keepcpo
339" vim: ts=4 fdm=marker
340