Skip to content

Instantly share code, notes, and snippets.

@Houl
Created August 11, 2019 16:39
Show Gist options
  • Save Houl/d28f6b05f14956296fd9328e149d8751 to your computer and use it in GitHub Desktop.
Save Houl/d28f6b05f14956296fd9328e149d8751 to your computer and use it in GitHub Desktop.
CallOverloaded()
" Emulate function overloading.
" File: overload.vim
" Created: 2014 Aug 01
" Last Change: 2018 Nov 18
" Version: 0.9
" Author: Andy Wokula <[email protected]>
" License: Vim License, see :h license
" CallOverloaded({func-dict}, {arg-list}, {dict})
"
" call one of the functions from {func-dict} with arguments {arg-list}.
" The types of {arg-list} values determine which function will be called.
" Basically, the function name is a concatenation of detected argument
" types. {dict} becomes the dict argument of the function call.
"
" For example, CallOverloaded(d, ['str'], {}) will attempt to call:
" d.S('str') (from high to low priority)
" d.S_('str') Hints: `S' = (string)
" d.X('str') `_' = optional arguments
" d.X_('str') `X' = (number or string)
" d._('str')
" The function name imposes the function signature:
" d.S(str)
" d.S_(str, ...)
" d.X(num_or_str)
" d.X_(num_or_str, ...)
" d._(...)
" At least one of these functions must be defined for the call to succeed.
" Hint: d._(...) should be defined to catch invalid arguments.
"
" OverloadToken({arg-list})
"
" return the function name you can use in the function dict to exactly
" match the given arguments {arg-list} (list).
" (helper func)
"
" Examples:
" :echo OverloadToken(['a', 1, 2, [], {}, function('tr'), 1.2])
" => SNNLDFR
"
" :echo OverloadToken([])
" => E
"
" OverloadFunc({funcnames}, {ovltoken})
"
" return the best match for {ovltoken} within {funcnames}.
" (helper func)
" {funcnames} (list of string) names of the user-defined dict
" functions, assumed to be sorted (using sort())
" {ovltoken} (string) result of OverloadToken()
"
" Example:
" :echo OverloadFunc(sort(['NS_', 'NX', 'N_']), 'NN')
" => NX
"
" OverloadInfo()
"
" return a dict with overloading meta data.
" (helper func)
"
" Notes:
" * `X' to match (number or string) is a special case. Vim often
" auto-converts between number and string, in which case it was annoying
" to be urged to specify different functions. Especially because each
" further such argument doubles the number of additional functions.
" * Can only check against basic types ... eg cannot look inside a
" dictionary to do some "pattern matching" etc. Will not add pattern
" matching, but: by convention, a lot of `Is*()' functions exist to check
" against a specific type ...
" History:
" 2017 Nov 03 extracted OverloadFunc(), pattern fix
" 2017 Aug 11 added new types (untested)
" (!) no longer call .Default()
" 2016 Feb 16 added `X' (N => [NX], S => [SX]) ... check dict argument for
" flag which specifies conversion of `S' and `N' to `X'
" (number or string)
" 2015 Oct 16 funcs `SSO' and `SO' => `SO' was called -- bug, must call
" the most specific function => change `O' into `_'.
" Examples:
" asneeded\femu\sutr-dispatch-example.vim
" SearchHierarchy(), asneeded\trial\D1845.vim
" asneeded\trial\D2046.vim
" Constants: {{{
" Mapping of type() results to identifier chars (for use in function name)
" Mnemonics: String, Number, List, Dict, Funcref, Real number
if exists('v:t_number')
let s:TYPEMAP = {v:t_string : 'S', v:t_number : 'N', v:t_list : 'L', v:t_dict : 'D', v:t_func : 'F', v:t_float : 'R', v:t_bool : 'B', v:t_none : 'O', v:t_job : 'J', v:t_channel : 'C'}
else
let s:TYPEMAP = {1: 'S', 0: 'N', 3: 'L', 4: 'D', 2: 'F', 5: 'R'}
endif
" optional arguments suffix (must start with a char GT any identifier char)
let s:OPTSFX = '_'
" name of the function to call when there are no arguments (not required)
let s:FNEMPTY = 'E'
" identifier char for unknown type (new type not yet recognized by this script)
let s:TUNKNOWN = 'U'
" identifier char for 'number or string' (must be GT 'N' and GT 'S')
let s:NUMORSTR = 'X'
"}}}
func! CallOverloaded(funcdict, arglist, dict) "{{{
let dt = OverloadToken(a:arglist)
if has_key(a:funcdict, dt)
let fn = dt
else
let fn = OverloadFunc(sort(keys(a:funcdict)), dt)
endif
return call(a:funcdict[fn], a:arglist, a:dict)
endfunc "}}}
func! OverloadToken(arglist) "{{{
let dt = join(map(range(len(a:arglist)), 'get(s:TYPEMAP, type(a:arglist[v:val]), s:TUNKNOWN)'), "")
return dt=="" ? s:FNEMPTY : dt
endfunc "}}}
func! OverloadFunc(funcnames, ftoken) "{{{
let gft = substitute(a:ftoken, '[NS]', '[&X]', 'g')
let pat = '^\%('. gft. '\|\%['. gft. ']'. s:OPTSFX. '\)$'
let dt = matchstr(a:funcnames, pat)
return dt=="" ? s:OPTSFX : dt
endfunc "}}}
func! OverloadInfo() "{{{
return {'optsfx': s:OPTSFX, 'fnempty': s:FNEMPTY, 'typemap': copy(s:TYPEMAP), 'tunknown': s:TUNKNOWN, 'tnumorstr': s:NUMORSTR}
endfunc "}}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment