VimScriptでbrainfuck処理系
VimScriptのお勉強も兼ねて.
.vim/pluginディレクトリに入れて,.vimrcに
nnoremap <C-i><C-f> :<C-u>call<Space>Bfrun_current_buffer()<CR>
と書けば,Ctrl-i Ctrl-fと打つだけで,今開いているバッファがbrainfuck
インタプリタで実行され,出力が新しいバッファとして開かれるようになります.(quickrunみたいな感じ)
結構うんこーどなので,変なところあったら言ってください.
" 対応するかぎかっこの位置を " 辞書型として返す function! s:Bfjumplist(program) let jumplist = {} for i in range(strlen(a:program)) if a:program[i] ==# '[' let j = 0 let depth = 0 while 1 if a:program[i+j] ==# '[' let depth += 1 elseif a:program[i+j] ==# ']' let depth -= 1 if depth ==# 0 let jumplist[i] = i+j break endif endif let j+=1 endwhile elseif a:program[i] ==# ']' let j = 0 let depth = 0 while 1 if a:program[i+j] ==# '[' let depth += 1 if depth ==# 0 let jumplist[i] = i+j break endif elseif a:program[i+j] ==# ']' let depth -= 1 endif let j-=1 endwhile else endif endfor return jumplist endfunction " BF実行関数 " 戻り値は出力 function! g:Bfrun(program, input) " プログラムカウンタ let pc = 0 let next_pc = 0 " メモリ let mem = map(range(30000), 0) " メモリのポインタ let ptr = 0 " ジャンプリスト let jumplist = s:Bfjumplist(a:program) " 出力 let output = '' while pc !=# len(a:program) if a:program[pc] ==# '>' let ptr += 1 let next_pc = pc + 1 elseif a:program[pc] ==# '<' let ptr -= 1 let next_pc = pc + 1 elseif a:program[pc] ==# '+' let mem[ptr] += 1 let next_pc = pc + 1 elseif a:program[pc] ==# '-' let mem[ptr] -= 1 let next_pc = pc + 1 elseif a:program[pc] ==# '.' let output = output.nr2char(mem[ptr]) let next_pc = pc + 1 elseif a:program[pc] ==# ',' let mem[ptr] = char2nr(a:input[0]) let a:input = a:input[1:] let next_pc = pc + 1 elseif a:program[pc] ==# '[' if mem[ptr] let next_pc = pc + 1 else let next_pc = jumplist[pc]+1 endif elseif a:program[pc] ==# ']' let next_pc = jumplist[pc] else let next_pc = pc + 1 endif let pc = next_pc endwhile return output endfunction " カレントバッファをソースコードとしてBfrunを呼び出す関数 " 結果は画面を立て分割して表示される function! Bfrun_current_buffer() let output = g:Bfrun(join(getline("1", "$"), ""), "") vsplit newfile call setline(1, split(output, "\n")) endfunction
おまけ
nnoremap <C-i><C-f> :<C-u>call<Space>Bf2c_current_buffer()<CR>
function! g:Bf2c(program, input) " インデントの深さ let depth = 0 " 出力 let output = '' " ヘッダ let output .= repeat("\t", depth)."#include <stdio.h>\n" let output .= repeat("\t", depth)."int main(void)\n" let output .= repeat("\t", depth)."{\n" let depth += 1 " メモリ let output .= repeat("\t", depth)."int mem[30000] = {0};\n" " メモリのポインタ let output .= repeat("\t", depth)."int ptr = 0;\n" " 入力文字バッファ let output .= repeat("\t", depth)."unsigned char c;\n" let i = 0 while i < strlen(a:program) if a:program[i] ==# '>' let cnt = 0 while a:program[i] ==# '>' let cnt += 1 let i += 1 endwhile let output .= repeat("\t", depth)."ptr += ".cnt.";\n" elseif a:program[i] ==# '<' let cnt = 0 while a:program[i] ==# '<' let cnt += 1 let i += 1 endwhile let output .= repeat("\t", depth)."ptr -= ".cnt.";\n" elseif a:program[i] ==# '+' let cnt = 0 while a:program[i] ==# '+' let cnt += 1 let i += 1 endwhile let output .= repeat("\t", depth)."mem[ptr] += ".cnt.";\n" elseif a:program[i] ==# '-' let cnt = 0 while a:program[i] ==# '-' let cnt += 1 let i += 1 endwhile let output .= repeat("\t", depth)."mem[ptr] -= ".cnt.";\n" elseif a:program[i] ==# '.' let output .= repeat("\t", depth)."printf(\"%c\", mem[ptr]);\n" let i += 1 elseif a:program[i] ==# ',' let output .= repeat("\t", depth)."scanf(\"%c\", &mem[ptr]);\n" let i += 1 elseif a:program[i] ==# '[' let output .= repeat("\t", depth)."while(mem[ptr]){\n" let depth += 1 let i += 1 elseif a:program[i] ==# ']' let depth -= 1 let output .= repeat("\t", depth)."}\n" let i += 1 else let i += 1 endif endwhile " フッタ let depth -= 1 let output .= repeat("\t", depth)."}\n" return output endfunction " カレントバッファをソースコードとしてBf2cを呼び出す関数 " 結果は画面を立て分割して表示される function! Bf2c_current_buffer() let output = g:Bf2c(join(getline("1", "$"), ""), "") vsplit newfile call setline(1, split(output, "\n")) endfunction