Vim script でパターンマッチ
を書いてみた。
構文は Scala を参考に。
[ソース]
function! s:func(n) Match a:n Case 1 => echo "one" Case 2 => echo "two" Case 3 => echo "three" EndMatch endfunction function! s:typename(a) Match type(a:a) Case type(0) => "integer" Case type("") => "string" Case type([]) => "list" Case _ => "other" EndMatch result return result endfunction function! s:fizzbuzz(n) Match [a:n%3 == 0, a:n%5 == 0] Case [1, 1] => "FizzBuzz" Case [1, _] => "Fizz" Case [_, 1] => "Buzz" Case _ => a:n EndMatch result return result endfunction function! s:main() call s:func(1) call s:func(2) call s:func(3) echo s:typename(1) echo s:typename("hoge") echo s:typename([1, 2, 3]) echo s:typename({}) echo s:typename(0.0) echo map(range(1, 20), "s:fizzbuzz(v:val)") endfunction call s:main()
[出力]
one two three integer string list other other [1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz', 11, 'Fizz', 13, 14, 'FizzBuzz', 16, 17, 'Fizz', 19, 'Buzz']
見た目はだいぶすっきり。
コマンドで処理を展開しています。
コマンドなので s: は使えません。かなしい。
Scala みたいに直接値を返したいんですが、コマンドだと無理なので、EndMatch で結果を受け取るような構文になっています。
これも何とかしたいな。
関数の戻り値にする場合もいちいち return が必要だからなぁ…。
[実装]
let s:default_pattern = "all_match" function! s:is_default(pattern) try return type(a:pattern) != type([]) && type(a:pattern) != type({}) && a:pattern =~ s:default_pattern catch return 0 endtry endfunction function! s:is_match(case, pattern) try return (type(a:case) == type([]) ? count(map(copy(a:case), "s:is_default(a:pattern[v:key]) || v:val ==# a:pattern[v:key]"), 1) == len(a:case) : a:case =~ a:pattern) catch return 0 endtry endfunction command! -nargs=1 Match \ let match_src = <args> \| let is_matched = 0 \| let _ = s:default_pattern command! -nargs=? EndMatch \ unlet match_src \| unlet is_matched \| unlet _ \| if !empty(<q-args>) && has_key(l:, "match_result") \| let <args> = match_result \| unlet match_result \| endif function! s:pattern(src) return matchstr(a:src, '\s*\zs.*\ze\s*=>.*') endfunction function! s:expr(src) return matchstr(a:src, '.*=>\s*\zs.*\ze\s*') endfunction command! -nargs=1 Case \ if !is_matched && (s:is_default(eval(s:pattern(<q-args>))) || s:is_match(match_src, eval(s:pattern(<q-args>)))) \| try \| execute "let match_result=" s:expr(<q-args>) \| catch \| execute s:expr(<q-args>) \| endtry \| let is_matched = 1 \| endif
だいぶ適当なので Advent Calendar の記事が書き終わったら直すんだ…。
……と、いうかこれが Advent ネタでも(ゲフンゲフン