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