crackNum-3.26: crackNum.vim
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" VI interface to crackNum
"
" Copyright : (c) Levent Erkok
" License : BSD3
" Maintainer : erkokl@gmail.com
"
" INSTALLATION: Put this file in a convenient location (typically your .vim directory),
" and put "so crackNum.vim" in your .vimrc file. (With the appropriate path.)
"
" Once you restart vim, locate your cursor over a stream of binary/hex digits, and
" enter the command :CrackNum to see further options.
"
" Arguments are passed to crackNum verbatim, so anything the executable accepts works:
"
" :CrackNum -i4 " decode as a 4-bit signed integer
" :CrackNum -fhp " decode as a half-precision float
" :CrackNum -f3+4 " decode as a float with 3 exponent, 4 significand bits
" :CrackNum -l4 -fhp " decode 4 lanes of half-precision floats
"
" Use TAB to complete the common formats. With no arguments, :CrackNum prompts for them.
" The formats offered come from "crackNum --list-formats", so they track the executable.
"
" Set g:crackNumProgram to run a crackNum that is not on your PATH, and
" g:crackNumPrecisions to replace the completion list outright.
"
" See https://github.com/LeventErkok/CrackNum for details.
" Which executable to run; override if crackNum is not on your PATH.
if !exists("g:crackNumProgram")
let g:crackNumProgram = "crackNum"
endif
" Integer widths and lane counts offered by TAB completion. These are only the common
" cases: crackNum takes any -iN/-wN/-lN, and any such argument can be typed in directly.
let s:crackNumIntFlags = ["-i8", "-i16", "-i32", "-i64", "-w8", "-w16", "-w32", "-w64"]
let s:crackNumLaneFlags = ["-l2", "-l4", "-l8"]
" Used only when crackNum is too old to know --list-formats, or is not on the PATH.
" Anything crackNum has learned since is picked up from the executable, not from here.
let s:crackNumFallbackFormats = [ "hp", "bp", "tf32", "sp", "dp", "qp"
\ , "e5m2", "e4m3", "fp4", "fp4e0m3"
\ ]
" The formats the executable reports, as -f flags. Asking it keeps this list from
" drifting out of date as crackNum grows new formats. Cached per executable, since
" completion is on a keystroke path and this shells out.
let s:crackNumFormatCache = {}
function! s:CrackNumFormats()
if has_key(s:crackNumFormatCache, g:crackNumProgram)
return s:crackNumFormatCache[g:crackNumProgram]
endif
let l:names = []
" 2>&1 so that an older crackNum, which errors out here, cannot scribble on the screen.
let l:out = systemlist(shellescape(g:crackNumProgram) . " --list-formats 2>&1")
if v:shell_error == 0
let l:names = filter(l:out, 'v:val =~# "^[a-z0-9]\\+$"')
endif
if empty(l:names)
let l:names = s:crackNumFallbackFormats
endif
let s:crackNumFormatCache[g:crackNumProgram] = map(copy(l:names), '"-f" . v:val')
return s:crackNumFormatCache[g:crackNumProgram]
endfunction
" What TAB offers. Set g:crackNumPrecisions yourself to override the whole list.
function! s:CrackNumChoices()
if exists("g:crackNumPrecisions")
return g:crackNumPrecisions
endif
return s:crackNumIntFlags + s:CrackNumFormats() + s:crackNumLaneFlags
endfunction
function! CrackNumComplete(A, L, P)
let l:all = s:CrackNumChoices()
if empty(a:A)
return l:all
endif
" Plain prefix match: the arguments contain '-' and '+', so avoid regex matching.
let l:out = filter(copy(l:all), 'stridx(v:val, a:A) == 0')
if empty(l:out)
return l:all
endif
return l:out
endfunction
" Patterns crackNum accepts as input, most specific first. Note that <cword> is not
" good enough on its own: it stops at the quote in verilog notation (64'hdeadbeef),
" and <cWORD> is too greedy, picking up trailing punctuation such as "0x8000;".
let s:crackNumPatterns = [ "\\d\\+'[bBoOdDhH][0-9a-fA-F_]\\+"
\ , "0[xX][0-9a-fA-F_]\\+"
\ , "0[bB][01_]\\+"
\ ]
" The bit-pattern under the cursor, falling back on <cword> if nothing matches.
function! s:CrackNumWord()
let l:line = getline('.')
let l:idx = col('.') - 1
for l:pat in s:crackNumPatterns
let l:from = 0
while 1
let l:m = matchstrpos(l:line, l:pat, l:from)
if l:m[1] < 0
break
endif
if l:idx >= l:m[1] && l:idx < l:m[2]
return l:m[0]
endif
let l:from = l:m[2]
endwhile
endfor
return expand("<cword>")
endfunction
function! CrackNum(...)
redraw
let l:curWord = s:CrackNumWord()
if empty(l:curWord)
echoerr "Place the cursor on a bin/hex number to crack!"
return
endif
if empty(a:000)
echo "Cracking \"" . l:curWord . "\".. Use TAB to see the formats supported."
call inputsave()
let l:args = [input("Format> ", "", "customlist,CrackNumComplete")]
call inputrestore()
redraw
if empty(join(l:args, ''))
echoerr "No format given; use e.g. -i4, -w8, or -fhp."
return
endif
else
echo "Cracking \"" . l:curWord . "\".."
let l:args = copy(a:000)
endif
" Quote the value: verilog notation (64'hdeadbeef) is not shell-safe as-is.
let l:grepargs = join(l:args + [shellescape(l:curWord)], ' ')
let l:grepprg_bak=&grepprg
let l:grepformat_bak=&grepformat
try
let &grepprg=g:crackNumProgram
" crackNum reports plain text, not file:line diagnostics, so take each line as is.
let &grepformat="%m"
silent execute "grep" . " " . l:grepargs
finally
let &grepprg=l:grepprg_bak
let &grepformat=l:grepformat_bak
endtry
call setqflist([], 'a', {'title': g:crackNumProgram . ' ' . l:grepargs})
botright copen
redraw!
endfunction
command! -nargs=* -complete=customlist,CrackNumComplete CrackNum call CrackNum(<f-args>)
" end crackNum interface