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