logiPat.vim 10 KB

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