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