vim script でクラスの定義 その2

色々と考えた結果こんな感じになりました。

[ソース]

let g:type = accel#type#type()

function! s:eval(class, expr, ...)
    let self = a:class
    if type(a:expr) == type(function("tr"))
        return call(a:expr, a:000, a:class)
    else
        return eval(a:expr)
    endif
endfunction

function! s:apply_def(class, def)
    let self = {}
    let self.class = a:class
    let self.def   = a:def

    function! self.apply(...)
        let expr = self.def.join(map(copy(a:000),"s:type(v:val)"), "_")
        let expr = s:has_key(self.class, expr) ? expr : self.def
        return call("s:eval", [self.class, self.class[expr]] + a:000)
    endfunction

    return self
endfunction

function! s:has_key(dict, key)
    return type(a:dict) == type({}) ? has_key(a:dict, a:key) : 0
endfunction

function! s:is_class(class)
    return s:has_key(a:class, "__type__")
endfunction

function! s:class(type)
    let self = {}
    let self.__type__ = "'".(a:type)."'"
    return self
endfunction

function! s:type(class)
    return s:is_class(a:class)
\        ? s:apply_def(a:class, "__type__").apply() : g:type.name(a:class)
"    型番号から文字列に変換
endfunction

function! s:apply(class)
    let self = s:apply_def(a:class, "__apply__")
    return self
endfunction

function! s:bool(class)
    return s:apply_def(a:class, "__bool__").apply()
endfunction

function! s:string(class)
    return s:is_class(a:class)
\        ? s:apply_def(a:class, "__string__").apply() : string(a:class)
endfunction

function! s:equal(class, other)
    return s:apply_def(a:class, "__equal__").apply(a:other)
endfunction

function! s:equal_type(a, b)
    return s:type(a:a) == s:type(a:b)
endfunction

function! s:plus(class, other)
    return s:apply_def(a:class, "__plus__").apply(a:other)
endfunction

" -----------------------------------------------------------------------------
" ユーザコード
function! s:vec()
    let self = s:class("vec")

    let self.x = 0
    let self.y = 0
    let self.z = 0
    
    function! self.__string__()
        return s:type(self)." : ".string([self.x, self.y, self.z])
    endfunction

    function! self.__equal__vec(vec)
        return self.x == a:vec.x && self.y == a:vec.y && self.z == a:vec.z
    endfunction

    function! self.__equal__(value)
        return self.x == a:value && self.y == a:value && self.z == a:vec
    endfunction
    
    function! self.__plus__vec(vec)
        let result = deepcopy(a:vec)
        let result.x += self.x
        let result.y += self.y
        let result.z += self.z
        return result
    
    endfunction

    function! self.__plus__(value)
        let result = deepcopy(self)
        let result.x += a:value
        let result.y += a:value
        let result.z += a:value
        return result
    endfunction
    
    return self
endfunction

function! s:make_vec(x, y, z)
    let result = s:vec()
    let result.x += a:x
    let result.y += a:y
    let result.z += a:z
    return result
endfunction


function! s:main()
    let vec = s:make_vec(0, 0, 0)
    echo s:string(vec)

    let vec = s:plus(vec, 2)
    echo s:string(vec)

    let vec = s:plus(vec, s:make_vec(1, 2, 3))
    echo s:string(vec)

    echo s:equal(vec, s:make_vec(3, 4, 5))
endfunction
call s:main()

[出力]

vec : [0, 0, 0]
vec : [2, 2, 2]
vec : [3, 4, 5]
1

どことなく Python っぽい?ような?
vim script には演算子オーバーロードの様な処理がないので、__string__ のような関数を定義して、s:string のような関数で、各処理が呼ばれるような形にしてみました。
演算子オーバーロードが欲しいと思う辺り C++脳ですね。


あと辞書の関数名に波括弧(先日書いたオーバーライド云々の書き方)が使えないんですね…。
あちゃーどうしようかなぁー。まぁ直接書いちゃってもいいんだけども…ぐぬぬ…。
詳細は、help expr-entry 辺りを。


とりあえず、現状はこんな感じにする予定。
またちょくちょく変わるかも?