aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZyX <kp-pav@yandex.ru>2015-08-30 14:12:47 +0300
committerZyX <kp-pav@yandex.ru>2015-11-01 21:27:27 +0300
commit00a638179d566f3bbbd95a618ecebcccb51a4126 (patch)
tree55a33d82fb65159108e9b7880265ec5961401d20
parent2e4baa9ae475e1ea01c5e15a440933b4814f0637 (diff)
downloadrneovim-00a638179d566f3bbbd95a618ecebcccb51a4126.tar.gz
rneovim-00a638179d566f3bbbd95a618ecebcccb51a4126.tar.bz2
rneovim-00a638179d566f3bbbd95a618ecebcccb51a4126.zip
runtime: Add autoload/msgpack.vim helper file
-rw-r--r--runtime/autoload/msgpack.vim820
-rw-r--r--test/functional/plugin/helpers.lua41
-rw-r--r--test/functional/plugin/msgpack_spec.lua684
3 files changed, 1545 insertions, 0 deletions
diff --git a/runtime/autoload/msgpack.vim b/runtime/autoload/msgpack.vim
new file mode 100644
index 0000000000..e6022922fe
--- /dev/null
+++ b/runtime/autoload/msgpack.vim
@@ -0,0 +1,820 @@
+if exists('g:loaded_msgpack_autoload')
+ finish
+endif
+let g:loaded_msgpack_autoload = 1
+
+""
+" Check that given value is an integer. Respects |msgpack-special-dict|.
+function msgpack#is_int(v) abort
+ return type(a:v) == type(0) || (
+ \type(a:v) == type({}) && get(a:v, '_TYPE') is# v:msgpack_types.integer)
+endfunction
+
+""
+" Check that given value is an unsigned integer. Respects
+" |msgpack-special-dict|.
+function msgpack#is_uint(v) abort
+ return msgpack#is_int(a:v) && (type(a:v) == type(0)
+ \? a:v >= 0
+ \: a:v._VAL[0] > 0)
+endfunction
+
+""
+" True if s:msgpack_init_python() function was already run.
+let s:msgpack_python_initialized = 0
+
+""
+" Cached return of s:msgpack_init_python() used when
+" s:msgpack_python_initialized is true.
+let s:msgpack_python_type = 0
+
+""
+" Create Python functions that are necessary for work. Also defines functions
+" s:msgpack_dict_strftime(format, timestamp) and s:msgpack_dict_strptime(format,
+" string).
+"
+" @return Zero in case no Python is available, empty string if Python-2 is
+" available and string `"3"` if Python-3 is available.
+function s:msgpack_init_python() abort
+ if s:msgpack_python_initialized
+ return s:msgpack_python_type
+ endif
+ let s:msgpack_python_initialized = 1
+ for suf in ['', '3']
+ try
+ execute 'python' . suf
+ \. "def shada_dict_strftime():\n"
+ \. " import datetime\n"
+ \. " import vim\n"
+ \. " fmt = vim.eval('a:format')\n"
+ \. " timestamp = vim.eval('a:timestamp')\n"
+ \. " timestamp = [int(v) for v in timestamp['_VAL']]\n"
+ \. " timestamp = timestamp[0] * (timestamp[1] << 62\n"
+ \. " | timestamp[2] << 31\n"
+ \. " | timestamp[3])\n"
+ \. " time = datetime.datetime.fromtimestamp(timestamp)\n"
+ \. " return time.strftime(fmt)\n"
+ \. "def shada_dict_strptime():\n"
+ \. " import datetime\n"
+ \. " import vim\n"
+ \. " fmt = vim.eval('a:format')\n"
+ \. " timestr = vim.eval('a:string')\n"
+ \. " timestamp = datetime.datetime.strptime(timestr, fmt)\n"
+ \. " timestamp = int(timestamp.timestamp())\n"
+ \. " if timestamp > 2 ** 31:\n"
+ \. " tsabs = abs(timestamp)"
+ \. " return ('{\"_TYPE\": v:msgpack_types.integer,'\n"
+ \. " + '\"_VAL\": [{sign},{v1},{v2},{v3}]}').format(\n"
+ \. " sign=1 if timestamp >= 0 else -1,\n"
+ \. " v1=((tsabs >> 62) & 0x3),\n"
+ \. " v2=((tsabs >> 31) & (2 ** 31 - 1)),\n"
+ \. " v3=(tsabs & (2 ** 31 - 1)))\n"
+ \. " else:\n"
+ \. " return str(timestamp)\n"
+ execute "function s:msgpack_dict_strftime(format, timestamp) abort\n"
+ \. " return py" . suf . "eval('shada_dict_strftime()')\n"
+ \. "endfunction\n"
+ \. "function s:msgpack_dict_strptime(format, string)\n"
+ \. " return eval(py" . suf . "eval('shada_dict_strptime()'))\n"
+ \. "endfunction\n"
+ let s:msgpack_python_type = suf
+ return suf
+ catch
+ continue
+ endtry
+ endfor
+
+ ""
+ " strftime() function for |msgpack-special-dict| values.
+ "
+ " @param[in] format String according to which time should be formatted.
+ " @param[in] timestamp Timestamp (seconds since epoch) to format.
+ "
+ " @return Formatted timestamp.
+ "
+ " @warning Without +python or +python3 this function does not work correctly.
+ " The VimL code contains “reference” implementation which does not
+ " really work because of precision loss.
+ function s:msgpack_dict_strftime(format, timestamp)
+ return msgpack#strftime(a:format, +msgpack#int_dict_to_str(a:timestamp))
+ endfunction
+
+ ""
+ " Function that parses given string according to given format.
+ "
+ " @param[in] format String according to which string was formatted.
+ " @param[in] string Time formatted according to format.
+ "
+ " @return Timestamp.
+ "
+ " @warning Without +python or +python3 this function is able to work only with
+ " 31-bit (32-bit signed) timestamps that have format
+ " `%Y-%m-%dT%H:%M:%S`.
+ function s:msgpack_dict_strptime(format, string)
+ let fmt = '%Y-%m-%dT%H:%M:%S'
+ if a:format isnot# fmt
+ throw 'notimplemented-format:Only ' . fmt . ' format is supported'
+ endif
+ let match = matchlist(a:string,
+ \'\v\C^(\d+)\-(\d+)\-(\d+)T(\d+)\:(\d+)\:(\d+)$')
+ if empty(match)
+ throw 'invalid-string:Given string does not match format ' . a:format
+ endif
+ call map(match, 'str2nr(v:val, 10)')
+ let [year, month, day, hour, minute, second] = match[1:6]
+ " Bisection start and end:
+ "
+ " Start: 365 days in year, 28 days in month, -12 hours tz offset.
+ let bisect_ts_start = (((((year - 1970) * 365
+ \+ (month - 1) * 28
+ \+ (day - 1)) * 24
+ \+ hour - 12) * 60
+ \+ minute) * 60
+ \+ second)
+ if bisect_ts_start < 0
+ let bisect_ts_start = 0
+ endif
+ let start_string = strftime(fmt, bisect_ts_start)
+ if start_string is# a:string
+ return bisect_ts_start
+ endif
+ " End: 366 days in year, 31 day in month, +14 hours tz offset.
+ let bisect_ts_end = (((((year - 1970) * 366
+ \+ (month - 1) * 31
+ \+ (day - 1)) * 24
+ \+ hour + 14) * 60
+ \+ minute) * 60
+ \+ second)
+ let end_string = strftime(fmt, bisect_ts_end)
+ if end_string is# a:string
+ return bisect_ts_end
+ endif
+ if start_string ># end_string
+ throw 'internal-start-gt:Internal error: start > end'
+ endif
+ if start_string is# end_string
+ throw printf('internal-start-eq:Internal error: '
+ \. 'start(%u)==end(%u), but start(%s)!=string(%s)',
+ \bisect_ts_start, bisect_ts_end,
+ \string(start_string), string(a:string))
+ endif
+ if start_string ># a:string
+ throw 'internal-start-string:Internal error: start > string'
+ endif
+ if end_string <# a:string
+ throw 'internal-end-string:Internal error: end < string'
+ endif
+ while 1
+ let bisect_ts_middle = (bisect_ts_start/2) + (bisect_ts_end/2)
+ let middle_string = strftime(fmt, bisect_ts_middle)
+ if a:string is# middle_string
+ return bisect_ts_middle
+ elseif a:string ># middle_string
+ if bisect_ts_middle == bisect_ts_start
+ let bisect_ts_start += 1
+ else
+ let bisect_ts_start = bisect_ts_middle
+ endif
+ else
+ if bisect_ts_middle == bisect_ts_end
+ let bisect_ts_end -= 1
+ else
+ let bisect_ts_end = bisect_ts_middle
+ endif
+ endif
+ if bisect_ts_start >= bisect_ts_end
+ throw 'not-found:Unable to find timestamp'
+ endif
+ endwhile
+ endfunction
+
+ return 0
+endfunction
+
+""
+" Wrapper for strftime() that respects |msgpack-special-dict|. May actually use
+" non-standard strftime() implementations for |msgpack-special-dict| values.
+"
+" @param[in] format Format string.
+" @param[in] timestamp Formatted timestamp.
+function msgpack#strftime(format, timestamp) abort
+ if type(a:timestamp) == type({})
+ call s:msgpack_init_python()
+ return s:msgpack_dict_strftime(a:format, a:timestamp)
+ else
+ return strftime(a:format, a:timestamp)
+ endif
+endfunction
+
+""
+" Parse string according to the format.
+"
+" Requires +python available. If it is not then only supported format is
+" `%Y-%m-%dT%H:%M:%S` because this is the format used by ShaDa plugin. Also in
+" this case bisection will be used (timestamps tried with strftime() up until
+" result matches the string) and only 31-bit (signed 32-bit: with negative
+" timestamps being useless this leaves 31 bits) timestamps will be supported.
+"
+" @param[in] format Time format.
+" @param[in] string Parsed time string. Must match given format.
+"
+" @return Timestamp. Possibly as |msgpack-special-dict|.
+function msgpack#strptime(format, string) abort
+ call s:msgpack_init_python()
+ return s:msgpack_dict_strptime(a:format, a:string)
+endfunction
+
+let s:MSGPACK_HIGHEST_BIT = 1
+let s:MSGPACK_HIGHEST_BIT_NR = 0
+while s:MSGPACK_HIGHEST_BIT * 2 > 0
+ let s:MSGPACK_HIGHEST_BIT = s:MSGPACK_HIGHEST_BIT * 2
+ let s:MSGPACK_HIGHEST_BIT_NR += 1
+endwhile
+
+""
+" Shift given number by given amount of bits
+function s:shift(n, s) abort
+ if a:s == 0
+ return a:n
+ elseif a:s < 0
+ let ret = a:n
+ for _ in range(-a:s)
+ let ret = ret / 2
+ endfor
+ return ret
+ else
+ let ret = a:n
+ for i in range(a:s)
+ let new_ret = ret * 2
+ if new_ret < ret
+ " Overflow: remove highest bit
+ let ret = xor(s:MSGPACK_HIGHEST_BIT, ret) * 2
+ endif
+ let ret = new_ret
+ endfor
+ return ret
+ endif
+endfunction
+
+let s:msgpack_mask_cache = {
+ \s:MSGPACK_HIGHEST_BIT_NR : s:MSGPACK_HIGHEST_BIT - 1}
+
+""
+" Apply a mask where first m bits are ones and other are zeroes to a given
+" number
+function s:mask1(n, m) abort
+ if a:m > s:MSGPACK_HIGHEST_BIT_NR + 1
+ let m = s:MSGPACK_HIGHEST_BIT_NR + 1
+ else
+ let m = a:m
+ endif
+ if !has_key(s:msgpack_mask_cache, m)
+ let p = 0
+ for _ in range(m)
+ let p = p * 2 + 1
+ endfor
+ let s:msgpack_mask_cache[m] = p
+ endif
+ return and(a:n, s:msgpack_mask_cache[m])
+endfunction
+
+""
+" Convert |msgpack-special-dict| that represents integer value to a string. Uses
+" hexadecimal representation starting with 0x because it is the easiest to
+" convert to.
+function msgpack#int_dict_to_str(v) abort
+ let v = a:v._VAL
+ " 64-bit number:
+ " 0000000001111111111222222222233333333334444444444555555555566666
+ " 1234567890123456789012345678901234567890123456789012345678901234
+ " Split in _VAL:
+ " 0000000001111111111222222222233 3333333344444444445555555555666 66
+ " 1234567890123456789012345678901 2345678901234567890123456789012 34
+ " Split by hex digits:
+ " 0000 0000 0111 1111 1112 2222 2222 2333 3333 3334 4444 4444 4555 5555 5556 6666
+ " 1234 5678 9012 3456 7890 1234 5678 9012 3456 7890 1234 5678 9012 3456 7890 1234
+ "
+ " Total split:
+ " _VAL[3] _VAL[2] _VAL[1]
+ " ______________________________________ _______________________________________ __
+ " 0000 0000 0111 1111 1112 2222 2222 233 3 3333 3334 4444 4444 4555 5555 5556 66 66
+ " 1234 5678 9012 3456 7890 1234 5678 901 2 3456 7890 1234 5678 9012 3456 7890 12 34
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^
+ " g4 g3 g2 g1
+ " ********************************** *** * ********************************** ** **
+ " 1 2 3 4 5 6
+ " 1: s:mask1(v[3], 28): first 28 bits of _VAL[3]
+ " 2: s:shift(v[3], -28): last 3 bits of _VAL[3]
+ " 3: s:mask1(v[2], 1): first bit of _VAL[2]
+ " 4: s:mask1(s:shift(v[2], -1), 28): bits 2 .. 29 of _VAL[2]
+ " 5: s:shift(v[2], -29): last 2 bits of _VAL[2]
+ " 6: s:shift(v[1], 2): _VAL[1]
+ let g4 = printf('%07x', s:mask1(v[3], 28))
+ let g3 = printf('%01x', or(s:shift(v[3], -28), s:shift(s:mask1(v[2], 1), 3)))
+ let g2 = printf('%07x', s:mask1(s:shift(v[2], -1), 28))
+ let g1 = printf('%01x', or(s:shift(v[2], -29), s:shift(v[1], 2)))
+ return ((v[0] < 0 ? '-' : '') . '0x' . g1 . g2 . g3 . g4)
+endfunction
+
+""
+" True boolean value.
+let g:msgpack#true = {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}
+lockvar! g:msgpack#true
+
+""
+" False boolean value.
+let g:msgpack#false = {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}
+lockvar! g:msgpack#false
+
+""
+" NIL value.
+let g:msgpack#nil = {'_TYPE': v:msgpack_types.nil, '_VAL': 0}
+lockvar! g:msgpack#nil
+
+""
+" Deduce type of |msgpack-special-dict|.
+"
+" @return zero if given dictionary is not special or name of the key in
+" v:msgpack_types dictionary.
+function msgpack#special_type(v) abort
+ if type(a:v) != type({}) || !has_key(a:v, '_TYPE')
+ return 0
+ endif
+ for [k, v] in items(v:msgpack_types)
+ if a:v._TYPE is v
+ return k
+ endif
+ endfor
+ return 0
+endfunction
+
+""
+" Mapping that maps type() output to type names.
+let s:MSGPACK_STANDARD_TYPES = {
+ \type(0): 'integer',
+ \type(0.0): 'float',
+ \type(''): 'binary',
+ \type([]): 'array',
+ \type({}): 'map',
+\}
+
+""
+" Deduce type of one of items returned by msgpackparse().
+"
+" @return Name of a key in v:msgpack_types.
+function msgpack#type(v) abort
+ let special_type = msgpack#special_type(a:v)
+ if special_type is 0
+ return s:MSGPACK_STANDARD_TYPES[type(a:v)]
+ endif
+ return special_type
+endfunction
+
+""
+" Dump nil value.
+function s:msgpack_dump_nil(v) abort
+ return 'NIL'
+endfunction
+
+""
+" Dump boolean value.
+function s:msgpack_dump_boolean(v) abort
+ return a:v._VAL ? 'TRUE' : 'FALSE'
+endfunction
+
+""
+" Dump integer msgpack value.
+function s:msgpack_dump_integer(v) abort
+ if type(a:v) == type({})
+ return msgpack#int_dict_to_str(a:v)
+ else
+ return string(a:v)
+ endif
+endfunction
+
+""
+" Dump floating-point value.
+function s:msgpack_dump_float(v) abort
+ return string(type(a:v) == type({}) ? a:v._VAL : a:v)
+endfunction
+
+""
+" Dump |msgpack-special-dict| that represents a string. If any additional
+" parameter is given then it dumps binary string.
+function s:msgpack_dump_string(v, ...) abort
+ let ret = [a:0 ? '"' : '="']
+ for v in a:v._VAL
+ call add(
+ \ret,
+ \substitute(
+ \substitute(v, '["\\]', '\\\0', 'g'),
+ \'\n', '\\0', 'g'))
+ call add(ret, '\n')
+ endfor
+ let ret[-1] = '"'
+ return join(ret, '')
+endfunction
+
+""
+" Dump binary string.
+function s:msgpack_dump_binary(v) abort
+ if type(a:v) == type({})
+ return s:msgpack_dump_string(a:v, 1)
+ else
+ return s:msgpack_dump_string({'_VAL': split(a:v, "\n", 1)}, 1)
+ endif
+endfunction
+
+""
+" Dump array value.
+function s:msgpack_dump_array(v) abort
+ let val = type(a:v) == type({}) ? a:v._VAL : a:v
+ return '[' . join(map(val[:], 'msgpack#string(v:val)'), ', ') . ']'
+endfunction
+
+""
+" Dump dictionary value.
+function s:msgpack_dump_map(v) abort
+ let ret = ['{']
+ if msgpack#special_type(a:v) is 0
+ for [k, v] in items(a:v)
+ let ret += [s:msgpack_dump_string({'_VAL': split(k, "\n", 1)}),
+ \': ',
+ \msgpack#string(v),
+ \', ']
+ unlet v
+ endfor
+ if !empty(a:v)
+ call remove(ret, -1)
+ endif
+ else
+ for [k, v] in sort(copy(a:v._VAL))
+ let ret += [msgpack#string(k),
+ \': ',
+ \msgpack#string(v),
+ \', ']
+ unlet k
+ unlet v
+ endfor
+ if !empty(a:v._VAL)
+ call remove(ret, -1)
+ endif
+ endif
+ let ret += ['}']
+ return join(ret, '')
+endfunction
+
+""
+" Dump extension value.
+function s:msgpack_dump_ext(v) abort
+ return printf('+(%i)%s', a:v._VAL[0],
+ \s:msgpack_dump_string({'_VAL': a:v._VAL[1]}, 1))
+endfunction
+
+""
+" Convert msgpack object to a string, like string() function does. Result of the
+" conversion may be passed to msgpack#eval().
+function msgpack#string(v) abort
+ if type(a:v) == type({})
+ let type = msgpack#special_type(a:v)
+ if type is 0
+ let type = 'map'
+ endif
+ else
+ let type = get(s:MSGPACK_STANDARD_TYPES, type(a:v), 0)
+ if type is 0
+ throw printf('msgpack:invtype: Unable to convert value %s', string(a:v))
+ endif
+ endif
+ return s:msgpack_dump_{type}(a:v)
+endfunction
+
+""
+" Copy msgpack object like deepcopy() does, but leave types intact
+function msgpack#deepcopy(obj) abort
+ if type(a:obj) == type([])
+ return map(copy(a:obj), 'msgpack#deepcopy(v:val)')
+ elseif type(a:obj) == type({})
+ let special_type = msgpack#special_type(a:obj)
+ if special_type is 0
+ return map(copy(a:obj), 'msgpack#deepcopy(v:val)')
+ else
+ return {
+ \'_TYPE': v:msgpack_types[special_type],
+ \'_VAL': msgpack#deepcopy(a:obj._VAL)
+ \}
+ endif
+ else
+ return copy(a:obj)
+ endif
+endfunction
+
+""
+" Convert an escaped character to needed value
+function s:msgpack_eval_str_sub(ch) abort
+ if a:ch is# 'n'
+ return '", "'
+ elseif a:ch is# '0'
+ return '\n'
+ else
+ return '\' . a:ch
+ endif
+endfunction
+
+let s:MSGPACK_SPECIAL_OBJECTS = {
+ \'NIL': '{''_TYPE'': v:msgpack_types.nil, ''_VAL'': 0}',
+ \'TRUE': '{''_TYPE'': v:msgpack_types.boolean, ''_VAL'': 1}',
+ \'FALSE': '{''_TYPE'': v:msgpack_types.boolean, ''_VAL'': 0}',
+ \'nan': '(-(1.0/0.0-1.0/0.0))',
+ \'inf': '(1.0/0.0)',
+\}
+
+""
+" Convert msgpack object dumped by msgpack#string() to a VimL object suitable
+" for msgpackdump().
+"
+" @param[in] s String to evaluate.
+" @param[in] special_objs Additional special objects, in the same format as
+" s:MSGPACK_SPECIAL_OBJECTS.
+"
+" @return Any value that msgpackparse() may return.
+function msgpack#eval(s, special_objs) abort
+ let s = a:s
+ let expr = []
+ let context = []
+ while !empty(s)
+ let s = substitute(s, '^\s*', '', '')
+ if s[0] =~# '\v^\h$'
+ let name = matchstr(s, '\v\C^\w+')
+ if has_key(s:MSGPACK_SPECIAL_OBJECTS, name)
+ call add(expr, s:MSGPACK_SPECIAL_OBJECTS[name])
+ elseif has_key(a:special_objs, name)
+ call add(expr, a:special_objs[name])
+ else
+ throw 'name-unknown:Unknown name ' . name . ': ' . s
+ endif
+ let s = s[len(name):]
+ elseif (s[0] is# '-' && s[1] =~# '\v^\d$') || s[0] =~# '\v^\d$'
+ let sign = 1
+ if s[0] is# '-'
+ let s = s[1:]
+ let sign = -1
+ endif
+ if s[0:1] is# '0x'
+ " See comment in msgpack#int_dict_to_str().
+ let s = s[2:]
+ let hexnum = matchstr(s, '\v\C^\x+')
+ if empty(hexnum)
+ throw '0x-empty:Must have number after 0x: ' . s
+ elseif len(hexnum) > 16
+ throw '0x-long:Must have at most 16 hex digits: ' . s
+ endif
+ let s = s[len(hexnum):]
+ let hexnum = repeat('0', 16 - len(hexnum)) . hexnum
+ let g1 = str2nr(hexnum[0], 16)
+ let g2 = str2nr(hexnum[1:7], 16)
+ let g3 = str2nr(hexnum[8], 16)
+ let g4 = str2nr(hexnum[9:15], 16)
+ let v1 = s:shift(g1, -2)
+ let v2 = or(or(s:shift(s:mask1(g1, 2), 29), s:shift(g2, 1)),
+ \s:mask1(s:shift(g3, -3), 1))
+ let v3 = or(s:shift(s:mask1(g3, 3), 28), g4)
+ call add(expr, printf('{''_TYPE'': v:msgpack_types.integer, '.
+ \'''_VAL'': [%i, %u, %u, %u]}',
+ \sign, v1, v2, v3))
+ else
+ let num = matchstr(s, '\v\C^\d+')
+ let s = s[len(num):]
+ if sign == -1
+ call add(expr, '-')
+ endif
+ call add(expr, num)
+ if s[0] is# '.'
+ let dec = matchstr(s, '\v\C^\.\d+%(e[+-]?\d+)?')
+ if empty(dec)
+ throw '0.-nodigits:Decimal dot must be followed by digit(s): ' . s
+ endif
+ let s = s[len(dec):]
+ call add(expr, dec)
+ endif
+ endif
+ elseif s =~# '-\?\%(inf\|nan\)'
+ if s[0] is# '-'
+ call add(expr, '-')
+ let s = s[1:]
+ endif
+ call add(expr, s:MSGPACK_SPECIAL_OBJECTS[s[0:2]])
+ let s = s[3:]
+ elseif stridx('="+', s[0]) != -1
+ let match = matchlist(s, '\v\C^(\=|\+\((\-?\d+)\)|)(\"%(\\.|[^\\"]+)*\")')
+ if empty(match)
+ throw '"-invalid:Invalid string: ' . s
+ endif
+ call add(expr, '{''_TYPE'': v:msgpack_types.')
+ if empty(match[1])
+ call add(expr, 'binary')
+ elseif match[1] is# '='
+ call add(expr, 'string')
+ else
+ call add(expr, 'ext')
+ endif
+ call add(expr, ', ''_VAL'': [')
+ if match[1][0] is# '+'
+ call add(expr, match[2] . ', [')
+ endif
+ call add(expr, substitute(match[3], '\v\C\\(.)',
+ \'\=s:msgpack_eval_str_sub(submatch(1))', 'g'))
+ if match[1][0] is# '+'
+ call add(expr, ']')
+ endif
+ call add(expr, ']}')
+ let s = s[len(match[0]):]
+ elseif s[0] is# '{'
+ call add(context, 'map')
+ call add(expr, '{''_TYPE'': v:msgpack_types.map, ''_VAL'': [')
+ call add(expr, '[')
+ let s = s[1:]
+ elseif s[0] is# '['
+ call add(context, 'array')
+ call add(expr, '[')
+ let s = s[1:]
+ elseif s[0] is# ':'
+ call add(expr, ',')
+ let s = s[1:]
+ elseif s[0] is# ','
+ if context[-1] is# 'array'
+ call add(expr, ',')
+ else
+ call add(expr, '], [')
+ endif
+ let s = s[1:]
+ elseif s[0] is# ']'
+ call remove(context, -1)
+ call add(expr, ']')
+ let s = s[1:]
+ elseif s[0] is# '}'
+ call remove(context, -1)
+ if expr[-1] is# "\x5B"
+ call remove(expr, -1)
+ else
+ call add(expr, ']')
+ endif
+ call add(expr, ']}')
+ let s = s[1:]
+ elseif s[0] is# ''''
+ let char = matchstr(s, '\m\C^''\zs.\ze''')
+ if empty(char)
+ throw 'char-invalid:Invalid integer character literal format: ' . s
+ endif
+ call add(expr, char2nr(char))
+ let s = s[len(char) + 2:]
+ else
+ throw 'unknown:Invalid non-space character: ' . s
+ endif
+ endwhile
+ if empty(expr)
+ throw 'empty:Parsed string is empty'
+ endif
+ return eval(join(expr, ''))
+endfunction
+
+""
+" Check whether two msgpack values are equal
+function msgpack#equal(a, b)
+ let atype = msgpack#type(a:a)
+ let btype = msgpack#type(a:b)
+ if atype isnot# btype
+ return 0
+ endif
+ let aspecial = msgpack#special_type(a:a)
+ let bspecial = msgpack#special_type(a:b)
+ if aspecial is# bspecial
+ if aspecial is# 0
+ if type(a:a) == type({})
+ if len(a:a) != len(a:b)
+ return 0
+ endif
+ if !empty(filter(keys(a:a), '!has_key(a:b, v:val)'))
+ return 0
+ endif
+ for [k, v] in items(a:a)
+ if !msgpack#equal(v, a:b[k])
+ return 0
+ endif
+ unlet v
+ endfor
+ return 1
+ elseif type(a:a) == type([])
+ if len(a:a) != len(a:b)
+ return 0
+ endif
+ let i = 0
+ for asubval in a:a
+ if !msgpack#equal(asubval, a:b[i])
+ return 0
+ endif
+ let i += 1
+ unlet asubval
+ endfor
+ return 1
+ elseif type(a:a) == type(0.0)
+ return (a:a == a:a ? a:a == a:b : string(a:a) ==# string(a:b))
+ else
+ return a:a ==# a:b
+ endif
+ elseif aspecial is# 'map' || aspecial is# 'array'
+ if len(a:a._VAL) != len(a:b._VAL)
+ return 0
+ endif
+ let alist = aspecial is# 'map' ? sort(copy(a:a._VAL)) : a:a._VAL
+ let blist = bspecial is# 'map' ? sort(copy(a:b._VAL)) : a:b._VAL
+ let i = 0
+ for asubval in alist
+ let bsubval = blist[i]
+ if aspecial is# 'map'
+ if !(msgpack#equal(asubval[0], bsubval[0])
+ \&& msgpack#equal(asubval[1], bsubval[1]))
+ return 0
+ endif
+ else
+ if !msgpack#equal(asubval, bsubval)
+ return 0
+ endif
+ endif
+ let i += 1
+ unlet asubval
+ unlet bsubval
+ endfor
+ return 1
+ elseif aspecial is# 'nil'
+ return 1
+ elseif aspecial is# 'float'
+ return (a:a._VAL == a:a._VAL
+ \? (a:a._VAL == a:b._VAL)
+ \: (string(a:a._VAL) ==# string(a:b._VAL)))
+ else
+ return a:a._VAL ==# a:b._VAL
+ endif
+ else
+ if atype is# 'array'
+ let a = aspecial is 0 ? a:a : a:a._VAL
+ let b = bspecial is 0 ? a:b : a:b._VAL
+ return msgpack#equal(a, b)
+ elseif atype is# 'binary'
+ let a = (aspecial is 0 ? split(a:a, "\n", 1) : a:a._VAL)
+ let b = (bspecial is 0 ? split(a:b, "\n", 1) : a:b._VAL)
+ return a ==# b
+ elseif atype is# 'map'
+ if aspecial is 0
+ let akeys = copy(a:a)
+ if len(a:b._VAL) != len(akeys)
+ return 0
+ endif
+ for [k, v] in a:b._VAL
+ if msgpack#type(k) isnot# 'string'
+ " Non-special mapping cannot have non-string keys
+ return 0
+ endif
+ if (empty(k._VAL)
+ \|| k._VAL ==# [""]
+ \|| !empty(filter(copy(k._VAL), 'stridx(v:val, "\n") != -1')))
+ " Non-special mapping cannot have zero byte in key or an empty key
+ return 0
+ endif
+ let kstr = join(k._VAL, "\n")
+ if !has_key(akeys, kstr)
+ " Protects from both missing and duplicate keys
+ return 0
+ endif
+ if !msgpack#equal(akeys[kstr], v)
+ return 0
+ endif
+ call remove(akeys, kstr)
+ unlet k
+ unlet v
+ endfor
+ return 1
+ else
+ return msgpack#equal(a:b, a:a)
+ endif
+ elseif atype is# 'float'
+ let a = aspecial is 0 ? a:a : a:a._VAL
+ let b = bspecial is 0 ? a:b : a:b._VAL
+ return (a == a ? a == b : string(a) ==# string(b))
+ elseif atype is# 'integer'
+ if aspecial is 0
+ let sign = a:a >= 0 ? 1 : -1
+ let a = sign * a:a
+ let v1 = s:mask1(s:shift(a, -62), 2)
+ let v2 = s:mask1(s:shift(a, -31), 31)
+ let v3 = s:mask1(a, 31)
+ return [sign, v1, v2, v3] == a:b._VAL
+ else
+ return msgpack#equal(a:b, a:a)
+ endif
+ else
+ throw printf('internal-invalid-type: %s == %s, but special %s /= %s',
+ \atype, btype, aspecial, bspecial)
+ endif
+ endif
+endfunction
diff --git a/test/functional/plugin/helpers.lua b/test/functional/plugin/helpers.lua
new file mode 100644
index 0000000000..217d561591
--- /dev/null
+++ b/test/functional/plugin/helpers.lua
@@ -0,0 +1,41 @@
+local paths = require('test.config.paths')
+
+local helpers = require('test.functional.helpers')
+local spawn, set_session, nvim_prog, merge_args =
+ helpers.spawn, helpers.set_session, helpers.nvim_prog, helpers.merge_args
+
+local additional_cmd = ''
+
+local function nvim_argv()
+ local rtp_value = ('\'%s/runtime\''):format(
+ paths.test_source_path:gsub('\'', '\'\''))
+ local nvim_argv = {nvim_prog, '-u', 'NORC', '-i', 'NONE', '-N',
+ '--cmd', 'set shortmess+=I background=light noswapfile',
+ '--cmd', 'let &runtimepath=' .. rtp_value,
+ '--cmd', additional_cmd,
+ '--embed'}
+ if helpers.prepend_argv then
+ return merge_args(helpers.prepend_argv, nvim_argv)
+ else
+ return nvim_argv
+ end
+end
+
+local session = nil
+
+local reset = function()
+ if session then
+ session:exit(0)
+ end
+ session = spawn(nvim_argv())
+ set_session(session)
+end
+
+local set_additional_cmd = function(s)
+ additional_cmd = s
+end
+
+return {
+ reset=reset,
+ set_additional_cmd=set_additional_cmd,
+}
diff --git a/test/functional/plugin/msgpack_spec.lua b/test/functional/plugin/msgpack_spec.lua
new file mode 100644
index 0000000000..7fd280e703
--- /dev/null
+++ b/test/functional/plugin/msgpack_spec.lua
@@ -0,0 +1,684 @@
+local helpers = require('test.functional.helpers')
+local eq, nvim_eval, nvim_command, exc_exec =
+ helpers.eq, helpers.eval, helpers.command, helpers.exc_exec
+
+local plugin_helpers = require('test.functional.plugin.helpers')
+local reset = plugin_helpers.reset
+
+describe('In autoload/msgpack.vim', function()
+ before_each(reset)
+
+ local sp = function(typ, val)
+ return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val)
+ end
+ local mapsp = function(...)
+ local val = ''
+ for i=1,(select('#', ...)/2) do
+ val = ('%s[%s,%s],'):format(val, select(i * 2 - 1, ...),
+ select(i * 2, ...))
+ end
+ return sp('map', '[' .. val .. ']')
+ end
+
+ describe('function msgpack#equal', function()
+ local msgpack_eq = function(expected, a, b)
+ eq(expected, nvim_eval(('msgpack#equal(%s, %s)'):format(a, b)))
+ if a ~= b then
+ eq(expected, nvim_eval(('msgpack#equal(%s, %s)'):format(b, a)))
+ end
+ end
+ it('compares raw integers correctly', function()
+ msgpack_eq(1, '1', '1')
+ msgpack_eq(0, '1', '0')
+ end)
+ it('compares integer specials correctly', function()
+ msgpack_eq(1, sp('integer', '[-1, 1, 0, 0]'),
+ sp('integer', '[-1, 1, 0, 0]'))
+ msgpack_eq(0, sp('integer', '[-1, 1, 0, 0]'),
+ sp('integer', '[ 1, 1, 0, 0]'))
+ end)
+ it('compares integer specials with raw integer correctly', function()
+ msgpack_eq(1, sp('integer', '[-1, 0, 0, 1]'), '-1')
+ msgpack_eq(0, sp('integer', '[-1, 0, 0, 1]'), '1')
+ msgpack_eq(0, sp('integer', '[ 1, 0, 0, 1]'), '-1')
+ msgpack_eq(1, sp('integer', '[ 1, 0, 0, 1]'), '1')
+ end)
+ it('compares integer with float correctly', function()
+ msgpack_eq(0, '0', '0.0')
+ end)
+ it('compares raw binaries correctly', function()
+ msgpack_eq(1, '"abc\\ndef"', '"abc\\ndef"')
+ msgpack_eq(0, '"abc\\ndef"', '"abc\\nghi"')
+ end)
+ it('compares binary specials correctly', function()
+ msgpack_eq(1, sp('binary', '["abc\\n", "def"]'),
+ sp('binary', '["abc\\n", "def"]'))
+ msgpack_eq(0, sp('binary', '["abc", "def"]'),
+ sp('binary', '["abc\\n", "def"]'))
+ end)
+ it('compares binary specials with raw binaries correctly', function()
+ msgpack_eq(1, sp('binary', '["abc", "def"]'), '"abc\\ndef"')
+ msgpack_eq(0, sp('binary', '["abc", "def"]'), '"abcdef"')
+ end)
+ it('compares string specials correctly', function()
+ msgpack_eq(1, sp('string', '["abc\\n", "def"]'),
+ sp('string', '["abc\\n", "def"]'))
+ msgpack_eq(0, sp('string', '["abc", "def"]'),
+ sp('string', '["abc\\n", "def"]'))
+ end)
+ it('compares string specials with binary correctly', function()
+ msgpack_eq(0, sp('string', '["abc\\n", "def"]'),
+ sp('binary', '["abc\\n", "def"]'))
+ msgpack_eq(0, sp('string', '["abc", "def"]'), '"abc\\ndef"')
+ msgpack_eq(0, sp('binary', '["abc\\n", "def"]'),
+ sp('string', '["abc\\n", "def"]'))
+ msgpack_eq(0, '"abc\\ndef"', sp('string', '["abc", "def"]'))
+ end)
+ it('compares ext specials correctly', function()
+ msgpack_eq(1, sp('ext', '[1, ["", "ac"]]'), sp('ext', '[1, ["", "ac"]]'))
+ msgpack_eq(0, sp('ext', '[2, ["", "ac"]]'), sp('ext', '[1, ["", "ac"]]'))
+ msgpack_eq(0, sp('ext', '[1, ["", "ac"]]'), sp('ext', '[1, ["", "abc"]]'))
+ end)
+ it('compares raw maps correctly', function()
+ msgpack_eq(1, '{"a": 1, "b": 2}', '{"b": 2, "a": 1}')
+ msgpack_eq(1, '{}', '{}')
+ msgpack_eq(0, '{}', '{"a": 1}')
+ msgpack_eq(0, '{"a": 2}', '{"a": 1}')
+ msgpack_eq(0, '{"a": 1}', '{"b": 1}')
+ msgpack_eq(0, '{"a": 1}', '{"a": 1, "b": 1}')
+ msgpack_eq(0, '{"a": 1, "b": 1}', '{"b": 1}')
+ end)
+ it('compares map specials correctly', function()
+ msgpack_eq(1, mapsp(), mapsp())
+ msgpack_eq(1, mapsp(sp('binary', '[""]'), '""'),
+ mapsp(sp('binary', '[""]'), '""'))
+ msgpack_eq(1, mapsp(mapsp('1', '1'), mapsp('1', '1')),
+ mapsp(mapsp('1', '1'), mapsp('1', '1')))
+ msgpack_eq(0, mapsp(), mapsp('1', '1'))
+ msgpack_eq(0, mapsp(sp('binary', '["a"]'), '""'),
+ mapsp(sp('binary', '[""]'), '""'))
+ msgpack_eq(0, mapsp(sp('binary', '[""]'), '"a"'),
+ mapsp(sp('binary', '[""]'), '""'))
+ msgpack_eq(0, mapsp(sp('binary', '["a"]'), '"a"'),
+ mapsp(sp('binary', '[""]'), '""'))
+ msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')),
+ mapsp(sp('binary', '[""]'), mapsp('1', '1')))
+ msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')),
+ mapsp(mapsp('2', '1'), mapsp('1', '1')))
+ msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')),
+ mapsp(mapsp('1', '2'), mapsp('1', '1')))
+ msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')),
+ mapsp(mapsp('1', '1'), mapsp('2', '1')))
+ msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')),
+ mapsp(mapsp('1', '1'), mapsp('1', '2')))
+ msgpack_eq(1, mapsp(mapsp('2', '1'), mapsp('1', '1'),
+ mapsp('1', '1'), mapsp('1', '1')),
+ mapsp(mapsp('1', '1'), mapsp('1', '1'),
+ mapsp('2', '1'), mapsp('1', '1')))
+ end)
+ it('compares map specials with raw maps correctly', function()
+ msgpack_eq(1, mapsp(), '{}')
+ msgpack_eq(1, mapsp(sp('string', '["1"]'), '1'), '{"1": 1}')
+ msgpack_eq(1, mapsp(sp('string', '["1"]'), sp('integer', '[1, 0, 0, 1]')),
+ '{"1": 1}')
+ msgpack_eq(0, mapsp(sp('integer', '[1, 0, 0, 1]'), sp('string', '["1"]')),
+ '{1: "1"}')
+ msgpack_eq(0, mapsp('"1"', sp('integer', '[1, 0, 0, 1]')),
+ '{"1": 1}')
+ msgpack_eq(0,
+ mapsp(sp('string', '["1"]'), '1', sp('string', '["2"]'), '2'),
+ '{"1": 1}')
+ msgpack_eq(0, mapsp(sp('string', '["1"]'), '1'), '{"1": 1, "2": 2}')
+ end)
+ it('compares raw arrays correctly', function()
+ msgpack_eq(1, '[]', '[]')
+ msgpack_eq(0, '[]', '[1]')
+ msgpack_eq(1, '[1]', '[1]')
+ msgpack_eq(1, '[[[1]]]', '[[[1]]]')
+ msgpack_eq(0, '[[[2]]]', '[[[1]]]')
+ end)
+ it('compares array specials correctly', function()
+ msgpack_eq(1, sp('array', '[]'), sp('array', '[]'))
+ msgpack_eq(0, sp('array', '[]'), sp('array', '[1]'))
+ msgpack_eq(1, sp('array', '[1]'), sp('array', '[1]'))
+ msgpack_eq(1, sp('array', '[[[1]]]'), sp('array', '[[[1]]]'))
+ msgpack_eq(0, sp('array', '[[[1]]]'), sp('array', '[[[2]]]'))
+ end)
+ it('compares array specials with raw arrays correctly', function()
+ msgpack_eq(1, sp('array', '[]'), '[]')
+ msgpack_eq(0, sp('array', '[]'), '[1]')
+ msgpack_eq(1, sp('array', '[1]'), '[1]')
+ msgpack_eq(1, sp('array', '[[[1]]]'), '[[[1]]]')
+ msgpack_eq(0, sp('array', '[[[1]]]'), '[[[2]]]')
+ end)
+ it('compares raw floats correctly', function()
+ msgpack_eq(1, '0.0', '0.0')
+ msgpack_eq(1, '(1.0/0.0-1.0/0.0)', '(1.0/0.0-1.0/0.0)')
+ msgpack_eq(0, '(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)')
+ msgpack_eq(0, '-(1.0/0.0-1.0/0.0)', '(1.0/0.0-1.0/0.0)')
+ msgpack_eq(1, '-(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)')
+ msgpack_eq(1, '1.0/0.0', '1.0/0.0')
+ msgpack_eq(1, '-(1.0/0.0)', '-(1.0/0.0)')
+ msgpack_eq(1, '0.0', '0.0')
+ msgpack_eq(0, '0.0', '1.0')
+ msgpack_eq(0, '0.0', '(1.0/0.0-1.0/0.0)')
+ msgpack_eq(0, '0.0', '1.0/0.0')
+ msgpack_eq(0, '0.0', '-(1.0/0.0)')
+ msgpack_eq(0, '1.0/0.0', '-(1.0/0.0)')
+ msgpack_eq(0, '(1.0/0.0-1.0/0.0)', '-(1.0/0.0)')
+ msgpack_eq(0, '(1.0/0.0-1.0/0.0)', '1.0/0.0')
+ end)
+ it('compares float specials with raw floats correctly', function()
+ msgpack_eq(1, sp('float', '0.0'), '0.0')
+ msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'), '(1.0/0.0-1.0/0.0)')
+ msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), '-(1.0/0.0-1.0/0.0)')
+ msgpack_eq(0, sp('float', '-(1.0/0.0-1.0/0.0)'), '(1.0/0.0-1.0/0.0)')
+ msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'), '-(1.0/0.0-1.0/0.0)')
+ msgpack_eq(1, sp('float', '1.0/0.0'), '1.0/0.0')
+ msgpack_eq(1, sp('float', '-(1.0/0.0)'), '-(1.0/0.0)')
+ msgpack_eq(1, sp('float', '0.0'), '0.0')
+ msgpack_eq(0, sp('float', '0.0'), '1.0')
+ msgpack_eq(0, sp('float', '0.0'), '(1.0/0.0-1.0/0.0)')
+ msgpack_eq(0, sp('float', '0.0'), '1.0/0.0')
+ msgpack_eq(0, sp('float', '0.0'), '-(1.0/0.0)')
+ msgpack_eq(0, sp('float', '1.0/0.0'), '-(1.0/0.0)')
+ msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), '-(1.0/0.0)')
+ msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), '1.0/0.0')
+ end)
+ it('compares float specials correctly', function()
+ msgpack_eq(1, sp('float', '0.0'), sp('float', '0.0'))
+ msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'),
+ sp('float', '(1.0/0.0-1.0/0.0)'))
+ msgpack_eq(1, sp('float', '1.0/0.0'), sp('float', '1.0/0.0'))
+ msgpack_eq(1, sp('float', '-(1.0/0.0)'), sp('float', '-(1.0/0.0)'))
+ msgpack_eq(1, sp('float', '0.0'), sp('float', '0.0'))
+ msgpack_eq(0, sp('float', '0.0'), sp('float', '1.0'))
+ msgpack_eq(0, sp('float', '0.0'), sp('float', '(1.0/0.0-1.0/0.0)'))
+ msgpack_eq(0, sp('float', '0.0'), sp('float', '1.0/0.0'))
+ msgpack_eq(0, sp('float', '0.0'), sp('float', '-(1.0/0.0)'))
+ msgpack_eq(0, sp('float', '1.0/0.0'), sp('float', '-(1.0/0.0)'))
+ msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), sp('float', '-(1.0/0.0)'))
+ msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'),
+ sp('float', '-(1.0/0.0-1.0/0.0)'))
+ msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'),
+ sp('float', '-(1.0/0.0-1.0/0.0)'))
+ msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), sp('float', '1.0/0.0'))
+ end)
+ it('compares boolean specials correctly', function()
+ msgpack_eq(1, sp('boolean', '1'), sp('boolean', '1'))
+ msgpack_eq(0, sp('boolean', '1'), sp('boolean', '0'))
+ end)
+ it('compares nil specials correctly', function()
+ msgpack_eq(1, sp('nil', '1'), sp('nil', '0'))
+ end)
+ it('compares nil, boolean and integer values with each other correctly',
+ function()
+ msgpack_eq(0, sp('boolean', '1'), '1')
+ msgpack_eq(0, sp('boolean', '1'), sp('nil', '0'))
+ msgpack_eq(0, sp('boolean', '1'), sp('nil', '1'))
+ msgpack_eq(0, sp('boolean', '0'), sp('nil', '0'))
+ msgpack_eq(0, sp('boolean', '0'), '0')
+ msgpack_eq(0, sp('boolean', '0'), sp('integer', '[1, 0, 0, 0]'))
+ msgpack_eq(0, sp('boolean', '0'), sp('integer', '[1, 0, 0, 1]'))
+ msgpack_eq(0, sp('boolean', '1'), sp('integer', '[1, 0, 0, 1]'))
+ msgpack_eq(0, sp('nil', '0'), sp('integer', '[1, 0, 0, 0]'))
+ msgpack_eq(0, sp('nil', '0'), '0')
+ end)
+ end)
+
+ describe('function msgpack#is_int', function()
+ it('works', function()
+ eq(1, nvim_eval('msgpack#is_int(1)'))
+ eq(1, nvim_eval('msgpack#is_int(-1)'))
+ eq(1, nvim_eval(('msgpack#is_int(%s)'):format(
+ sp('integer', '[1, 0, 0, 1]'))))
+ eq(1, nvim_eval(('msgpack#is_int(%s)'):format(
+ sp('integer', '[-1, 0, 0, 1]'))))
+ eq(0, nvim_eval(('msgpack#is_int(%s)'):format(
+ sp('float', '0.0'))))
+ eq(0, nvim_eval(('msgpack#is_int(%s)'):format(
+ sp('boolean', '0'))))
+ eq(0, nvim_eval(('msgpack#is_int(%s)'):format(
+ sp('nil', '0'))))
+ eq(0, nvim_eval('msgpack#is_int("")'))
+ end)
+ end)
+
+ describe('function msgpack#is_uint', function()
+ it('works', function()
+ eq(1, nvim_eval('msgpack#is_uint(1)'))
+ eq(0, nvim_eval('msgpack#is_uint(-1)'))
+ eq(1, nvim_eval(('msgpack#is_uint(%s)'):format(
+ sp('integer', '[1, 0, 0, 1]'))))
+ eq(0, nvim_eval(('msgpack#is_uint(%s)'):format(
+ sp('integer', '[-1, 0, 0, 1]'))))
+ eq(0, nvim_eval(('msgpack#is_uint(%s)'):format(
+ sp('float', '0.0'))))
+ eq(0, nvim_eval(('msgpack#is_uint(%s)'):format(
+ sp('boolean', '0'))))
+ eq(0, nvim_eval(('msgpack#is_uint(%s)'):format(
+ sp('nil', '0'))))
+ eq(0, nvim_eval('msgpack#is_uint("")'))
+ end)
+ end)
+
+ describe('function msgpack#strftime', function()
+ it('works', function()
+ local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0)
+ eq(epoch, nvim_eval('msgpack#strftime("%Y-%m-%dT%H:%M:%S", 0)'))
+ eq(epoch, nvim_eval(
+ ('msgpack#strftime("%%Y-%%m-%%dT%%H:%%M:%%S", %s)'):format(sp(
+ 'integer', '[1, 0, 0, 0]'))))
+ end)
+ end)
+
+ describe('function msgpack#strptime', function()
+ it('works', function()
+ for _, v in ipairs({0, 10, 100000, 204, 1000000000}) do
+ local time = os.date('%Y-%m-%dT%H:%M:%S', v)
+ eq(v, nvim_eval('msgpack#strptime("%Y-%m-%dT%H:%M:%S", '
+ .. '"' .. time .. '")'))
+ end
+ end)
+ end)
+
+ describe('function msgpack#type', function()
+ local type_eq = function(expected, val)
+ eq(expected, nvim_eval(('msgpack#type(%s)'):format(val)))
+ end
+
+ it('works for special dictionaries', function()
+ type_eq('string', sp('string', '[""]'))
+ type_eq('binary', sp('binary', '[""]'))
+ type_eq('ext', sp('ext', '[1, [""]]'))
+ type_eq('array', sp('array', '[]'))
+ type_eq('map', sp('map', '[]'))
+ type_eq('integer', sp('integer', '[1, 0, 0, 0]'))
+ type_eq('float', sp('float', '0.0'))
+ type_eq('boolean', sp('boolean', '0'))
+ type_eq('nil', sp('nil', '0'))
+ end)
+
+ it('works for regular values', function()
+ type_eq('binary', '""')
+ type_eq('array', '[]')
+ type_eq('map', '{}')
+ type_eq('integer', '1')
+ type_eq('float', '0.0')
+ type_eq('float', '(1.0/0.0)')
+ type_eq('float', '-(1.0/0.0)')
+ type_eq('float', '(1.0/0.0-1.0/0.0)')
+ end)
+ end)
+
+ describe('function msgpack#special_type', function()
+ local sp_type_eq = function(expected, val)
+ eq(expected, nvim_eval(('msgpack#special_type(%s)'):format(val)))
+ end
+
+ it('works for special dictionaries', function()
+ sp_type_eq('string', sp('string', '[""]'))
+ sp_type_eq('binary', sp('binary', '[""]'))
+ sp_type_eq('ext', sp('ext', '[1, [""]]'))
+ sp_type_eq('array', sp('array', '[]'))
+ sp_type_eq('map', sp('map', '[]'))
+ sp_type_eq('integer', sp('integer', '[1, 0, 0, 0]'))
+ sp_type_eq('float', sp('float', '0.0'))
+ sp_type_eq('boolean', sp('boolean', '0'))
+ sp_type_eq('nil', sp('nil', '0'))
+ end)
+
+ it('works for regular values', function()
+ sp_type_eq(0, '""')
+ sp_type_eq(0, '[]')
+ sp_type_eq(0, '{}')
+ sp_type_eq(0, '1')
+ sp_type_eq(0, '0.0')
+ sp_type_eq(0, '(1.0/0.0)')
+ sp_type_eq(0, '-(1.0/0.0)')
+ sp_type_eq(0, '(1.0/0.0-1.0/0.0)')
+ end)
+ end)
+
+ describe('function msgpack#string', function()
+ local string_eq = function(expected, val)
+ eq(expected, nvim_eval(('msgpack#string(%s)'):format(val)))
+ end
+
+ it('works for special dictionaries', function()
+ string_eq('=""', sp('string', '[""]'))
+ string_eq('="\\n"', sp('string', '["", ""]'))
+ string_eq('="ab\\0c\\nde"', sp('string', '["ab\\nc", "de"]'))
+ string_eq('""', sp('binary', '[""]'))
+ string_eq('"\\n"', sp('binary', '["", ""]'))
+ string_eq('"ab\\0c\\nde"', sp('binary', '["ab\\nc", "de"]'))
+ string_eq('+(2)""', sp('ext', '[2, [""]]'))
+ string_eq('+(2)"\\n"', sp('ext', '[2, ["", ""]]'))
+ string_eq('+(2)"ab\\0c\\nde"', sp('ext', '[2, ["ab\\nc", "de"]]'))
+ string_eq('[]', sp('array', '[]'))
+ string_eq('[[[[{}]]]]', sp('array', '[[[[{}]]]]'))
+ string_eq('{}', sp('map', '[]'))
+ string_eq('{2: 10}', sp('map', '[[2, 10]]'))
+ string_eq('{{1: 1}: {1: 1}, {2: 1}: {1: 1}}',
+ mapsp(mapsp('2', '1'), mapsp('1', '1'),
+ mapsp('1', '1'), mapsp('1', '1')))
+ string_eq('{{1: 1}: {1: 1}, {2: 1}: {1: 1}}',
+ mapsp(mapsp('1', '1'), mapsp('1', '1'),
+ mapsp('2', '1'), mapsp('1', '1')))
+ string_eq('{[1, 2, {{1: 2}: 1}]: [1, 2, {{1: 2}: 1}]}',
+ mapsp(('[1, 2, %s]'):format(mapsp(mapsp('1', '2'), '1')),
+ ('[1, 2, %s]'):format(mapsp(mapsp('1', '2'), '1'))))
+ string_eq('0x0000000000000000', sp('integer', '[1, 0, 0, 0]'))
+ string_eq('-0x0000000100000000', sp('integer', '[-1, 0, 2, 0]'))
+ string_eq('0x123456789abcdef0',
+ sp('integer', '[ 1, 0, 610839793, 448585456]'))
+ string_eq('-0x123456789abcdef0',
+ sp('integer', '[-1, 0, 610839793, 448585456]'))
+ string_eq('0xf23456789abcdef0',
+ sp('integer', '[ 1, 3, 1684581617, 448585456]'))
+ string_eq('-0x723456789abcdef0',
+ sp('integer', '[-1, 1, 1684581617, 448585456]'))
+ string_eq('0.0', sp('float', '0.0'))
+ string_eq('inf', sp('float', '(1.0/0.0)'))
+ string_eq('-inf', sp('float', '-(1.0/0.0)'))
+ string_eq('-nan', sp('float', '(1.0/0.0-1.0/0.0)'))
+ string_eq('nan', sp('float', '-(1.0/0.0-1.0/0.0)'))
+ string_eq('FALSE', sp('boolean', '0'))
+ string_eq('TRUE', sp('boolean', '1'))
+ string_eq('NIL', sp('nil', '0'))
+ end)
+
+ it('works for regular values', function()
+ string_eq('""', '""')
+ string_eq('"\\n"', '"\\n"')
+ string_eq('[]', '[]')
+ string_eq('[[[{}]]]', '[[[{}]]]')
+ string_eq('{}', '{}')
+ string_eq('{="2": 10}', '{2: 10}')
+ string_eq('{="2": [{}]}', '{2: [{}]}')
+ string_eq('1', '1')
+ string_eq('0.0', '0.0')
+ string_eq('inf', '(1.0/0.0)')
+ string_eq('-inf', '-(1.0/0.0)')
+ string_eq('-nan', '(1.0/0.0-1.0/0.0)')
+ string_eq('nan', '-(1.0/0.0-1.0/0.0)')
+ end)
+ end)
+
+ describe('function msgpack#deepcopy', function()
+ it('works for special dictionaries', function()
+ nvim_command('let sparr = ' .. sp('array', '[[[]]]'))
+ nvim_command('let spmap = ' .. mapsp('"abc"', '[[]]'))
+ nvim_command('let spint = ' .. sp('integer', '[1, 0, 0, 0]'))
+ nvim_command('let spflt = ' .. sp('float', '1.0'))
+ nvim_command('let spext = ' .. sp('ext', '[2, ["abc", "def"]]'))
+ nvim_command('let spstr = ' .. sp('string', '["abc", "def"]'))
+ nvim_command('let spbin = ' .. sp('binary', '["abc", "def"]'))
+ nvim_command('let spbln = ' .. sp('boolean', '0'))
+ nvim_command('let spnil = ' .. sp('nil', '0'))
+
+ nvim_command('let sparr2 = msgpack#deepcopy(sparr)')
+ nvim_command('let spmap2 = msgpack#deepcopy(spmap)')
+ nvim_command('let spint2 = msgpack#deepcopy(spint)')
+ nvim_command('let spflt2 = msgpack#deepcopy(spflt)')
+ nvim_command('let spext2 = msgpack#deepcopy(spext)')
+ nvim_command('let spstr2 = msgpack#deepcopy(spstr)')
+ nvim_command('let spbin2 = msgpack#deepcopy(spbin)')
+ nvim_command('let spbln2 = msgpack#deepcopy(spbln)')
+ nvim_command('let spnil2 = msgpack#deepcopy(spnil)')
+
+ eq('array', nvim_eval('msgpack#type(sparr2)'))
+ eq('map', nvim_eval('msgpack#type(spmap2)'))
+ eq('integer', nvim_eval('msgpack#type(spint2)'))
+ eq('float', nvim_eval('msgpack#type(spflt2)'))
+ eq('ext', nvim_eval('msgpack#type(spext2)'))
+ eq('string', nvim_eval('msgpack#type(spstr2)'))
+ eq('binary', nvim_eval('msgpack#type(spbin2)'))
+ eq('boolean', nvim_eval('msgpack#type(spbln2)'))
+ eq('nil', nvim_eval('msgpack#type(spnil2)'))
+
+ nvim_command('call add(sparr._VAL, 0)')
+ nvim_command('call add(sparr._VAL[0], 0)')
+ nvim_command('call add(sparr._VAL[0][0], 0)')
+ nvim_command('call add(spmap._VAL, [0, 0])')
+ nvim_command('call add(spmap._VAL[0][1], 0)')
+ nvim_command('call add(spmap._VAL[0][1][0], 0)')
+ nvim_command('let spint._VAL[1] = 1')
+ nvim_command('let spflt._VAL = 0.0')
+ nvim_command('let spext._VAL[0] = 3')
+ nvim_command('let spext._VAL[1][0] = "gh"')
+ nvim_command('let spstr._VAL[0] = "gh"')
+ nvim_command('let spbin._VAL[0] = "gh"')
+ nvim_command('let spbln._VAL = 1')
+ nvim_command('let spnil._VAL = 1')
+
+ eq({_TYPE={}, _VAL={{{}}}}, nvim_eval('sparr2'))
+ eq({_TYPE={}, _VAL={{'abc', {{}}}}}, nvim_eval('spmap2'))
+ eq({_TYPE={}, _VAL={1, 0, 0, 0}}, nvim_eval('spint2'))
+ eq({_TYPE={}, _VAL=1.0}, nvim_eval('spflt2'))
+ eq({_TYPE={}, _VAL={2, {'abc', 'def'}}}, nvim_eval('spext2'))
+ eq({_TYPE={}, _VAL={'abc', 'def'}}, nvim_eval('spstr2'))
+ eq({_TYPE={}, _VAL={'abc', 'def'}}, nvim_eval('spbin2'))
+ eq({_TYPE={}, _VAL=0}, nvim_eval('spbln2'))
+ eq({_TYPE={}, _VAL=0}, nvim_eval('spnil2'))
+
+ nvim_command('let sparr._TYPE = []')
+ nvim_command('let spmap._TYPE = []')
+ nvim_command('let spint._TYPE = []')
+ nvim_command('let spflt._TYPE = []')
+ nvim_command('let spext._TYPE = []')
+ nvim_command('let spstr._TYPE = []')
+ nvim_command('let spbin._TYPE = []')
+ nvim_command('let spbln._TYPE = []')
+ nvim_command('let spnil._TYPE = []')
+
+ eq('array', nvim_eval('msgpack#special_type(sparr2)'))
+ eq('map', nvim_eval('msgpack#special_type(spmap2)'))
+ eq('integer', nvim_eval('msgpack#special_type(spint2)'))
+ eq('float', nvim_eval('msgpack#special_type(spflt2)'))
+ eq('ext', nvim_eval('msgpack#special_type(spext2)'))
+ eq('string', nvim_eval('msgpack#special_type(spstr2)'))
+ eq('binary', nvim_eval('msgpack#special_type(spbin2)'))
+ eq('boolean', nvim_eval('msgpack#special_type(spbln2)'))
+ eq('nil', nvim_eval('msgpack#special_type(spnil2)'))
+ end)
+
+ it('works for regular values', function()
+ nvim_command('let arr = [[[]]]')
+ nvim_command('let map = {1: {}}')
+ nvim_command('let int = 1')
+ nvim_command('let flt = 2.0')
+ nvim_command('let bin = "abc"')
+
+ nvim_command('let arr2 = msgpack#deepcopy(arr)')
+ nvim_command('let map2 = msgpack#deepcopy(map)')
+ nvim_command('let int2 = msgpack#deepcopy(int)')
+ nvim_command('let flt2 = msgpack#deepcopy(flt)')
+ nvim_command('let bin2 = msgpack#deepcopy(bin)')
+
+ eq('array', nvim_eval('msgpack#type(arr2)'))
+ eq('map', nvim_eval('msgpack#type(map2)'))
+ eq('integer', nvim_eval('msgpack#type(int2)'))
+ eq('float', nvim_eval('msgpack#type(flt2)'))
+ eq('binary', nvim_eval('msgpack#type(bin2)'))
+
+ nvim_command('call add(arr, 0)')
+ nvim_command('call add(arr[0], 0)')
+ nvim_command('call add(arr[0][0], 0)')
+ nvim_command('let map.a = 1')
+ nvim_command('let map.1.a = 1')
+ nvim_command('let int = 2')
+ nvim_command('let flt = 3.0')
+ nvim_command('let bin = ""')
+
+ eq({{{}}}, nvim_eval('arr2'))
+ eq({['1']={}}, nvim_eval('map2'))
+ eq(1, nvim_eval('int2'))
+ eq(2.0, nvim_eval('flt2'))
+ eq('abc', nvim_eval('bin2'))
+ end)
+ end)
+
+ describe('function msgpack#eval', function()
+ local eval_eq = function(expected_type, expected_val, str, ...)
+ nvim_command(('let g:__val = msgpack#eval(\'%s\', %s)'):format(str:gsub(
+ '\'', '\'\''), select(1, ...) or '{}'))
+ eq(expected_type, nvim_eval('msgpack#type(g:__val)'))
+ local expected_val_full = expected_val
+ if (not (({float=true, integer=true})[expected_type]
+ and type(expected_val) ~= 'table')
+ and expected_type ~= 'array') then
+ expected_val_full = {_TYPE={}, _VAL=expected_val_full}
+ end
+ if expected_val_full == expected_val_full then
+ eq(expected_val_full, nvim_eval('g:__val'))
+ else
+ eq(tostring(expected_val_full), tostring(nvim_eval('g:__val')))
+ end
+ nvim_command('unlet g:__val')
+ end
+
+ it('correctly loads binary strings', function()
+ eval_eq('binary', {'abcdef'}, '"abcdef"')
+ eval_eq('binary', {'abc', 'def'}, '"abc\\ndef"')
+ eval_eq('binary', {'abc\ndef'}, '"abc\\0def"')
+ eval_eq('binary', {'\nabc\ndef\n'}, '"\\0abc\\0def\\0"')
+ eval_eq('binary', {'abc\n\n\ndef'}, '"abc\\0\\0\\0def"')
+ eval_eq('binary', {'abc\n', '\ndef'}, '"abc\\0\\n\\0def"')
+ eval_eq('binary', {'abc', '', '', 'def'}, '"abc\\n\\n\\ndef"')
+ eval_eq('binary', {'abc', '', '', 'def', ''}, '"abc\\n\\n\\ndef\\n"')
+ eval_eq('binary', {'', 'abc', '', '', 'def'}, '"\\nabc\\n\\n\\ndef"')
+ eval_eq('binary', {''}, '""')
+ eval_eq('binary', {'"'}, '"\\""')
+ end)
+
+ it('correctly loads strings', function()
+ eval_eq('string', {'abcdef'}, '="abcdef"')
+ eval_eq('string', {'abc', 'def'}, '="abc\\ndef"')
+ eval_eq('string', {'abc\ndef'}, '="abc\\0def"')
+ eval_eq('string', {'\nabc\ndef\n'}, '="\\0abc\\0def\\0"')
+ eval_eq('string', {'abc\n\n\ndef'}, '="abc\\0\\0\\0def"')
+ eval_eq('string', {'abc\n', '\ndef'}, '="abc\\0\\n\\0def"')
+ eval_eq('string', {'abc', '', '', 'def'}, '="abc\\n\\n\\ndef"')
+ eval_eq('string', {'abc', '', '', 'def', ''}, '="abc\\n\\n\\ndef\\n"')
+ eval_eq('string', {'', 'abc', '', '', 'def'}, '="\\nabc\\n\\n\\ndef"')
+ eval_eq('string', {''}, '=""')
+ eval_eq('string', {'"'}, '="\\""')
+ end)
+
+ it('correctly loads ext values', function()
+ eval_eq('ext', {0, {'abcdef'}}, '+(0)"abcdef"')
+ eval_eq('ext', {0, {'abc', 'def'}}, '+(0)"abc\\ndef"')
+ eval_eq('ext', {0, {'abc\ndef'}}, '+(0)"abc\\0def"')
+ eval_eq('ext', {0, {'\nabc\ndef\n'}}, '+(0)"\\0abc\\0def\\0"')
+ eval_eq('ext', {0, {'abc\n\n\ndef'}}, '+(0)"abc\\0\\0\\0def"')
+ eval_eq('ext', {0, {'abc\n', '\ndef'}}, '+(0)"abc\\0\\n\\0def"')
+ eval_eq('ext', {0, {'abc', '', '', 'def'}}, '+(0)"abc\\n\\n\\ndef"')
+ eval_eq('ext', {0, {'abc', '', '', 'def', ''}},
+ '+(0)"abc\\n\\n\\ndef\\n"')
+ eval_eq('ext', {0, {'', 'abc', '', '', 'def'}},
+ '+(0)"\\nabc\\n\\n\\ndef"')
+ eval_eq('ext', {0, {''}}, '+(0)""')
+ eval_eq('ext', {0, {'"'}}, '+(0)"\\""')
+
+ eval_eq('ext', {-1, {'abcdef'}}, '+(-1)"abcdef"')
+ eval_eq('ext', {-1, {'abc', 'def'}}, '+(-1)"abc\\ndef"')
+ eval_eq('ext', {-1, {'abc\ndef'}}, '+(-1)"abc\\0def"')
+ eval_eq('ext', {-1, {'\nabc\ndef\n'}}, '+(-1)"\\0abc\\0def\\0"')
+ eval_eq('ext', {-1, {'abc\n\n\ndef'}}, '+(-1)"abc\\0\\0\\0def"')
+ eval_eq('ext', {-1, {'abc\n', '\ndef'}}, '+(-1)"abc\\0\\n\\0def"')
+ eval_eq('ext', {-1, {'abc', '', '', 'def'}}, '+(-1)"abc\\n\\n\\ndef"')
+ eval_eq('ext', {-1, {'abc', '', '', 'def', ''}},
+ '+(-1)"abc\\n\\n\\ndef\\n"')
+ eval_eq('ext', {-1, {'', 'abc', '', '', 'def'}},
+ '+(-1)"\\nabc\\n\\n\\ndef"')
+ eval_eq('ext', {-1, {''}}, '+(-1)""')
+ eval_eq('ext', {-1, {'"'}}, '+(-1)"\\""')
+ end)
+
+ it('correctly loads floats', function()
+ eval_eq('float', 1.0/0.0, 'inf')
+ eval_eq('float', -1.0/0.0, '-inf')
+ eval_eq('float', -(1.0/0.0-1.0/0.0), 'nan')
+ eval_eq('float', (1.0/0.0-1.0/0.0), '-nan')
+ eval_eq('float', 1.0e10, '1.0e10')
+ eval_eq('float', 1.0e10, '1.0e+10')
+ eval_eq('float', -1.0e10, '-1.0e+10')
+ eval_eq('float', 1.0, '1.0')
+ eval_eq('float', -1.0, '-1.0')
+ eval_eq('float', 1.0e-10, '1.0e-10')
+ eval_eq('float', -1.0e-10, '-1.0e-10')
+ end)
+
+ it('correctly loads integers', function()
+ eval_eq('integer', 10, '10')
+ eval_eq('integer', -10, '-10')
+ eval_eq('integer', { 1, 0, 610839793, 448585456}, ' 0x123456789ABCDEF0')
+ eval_eq('integer', {-1, 0, 610839793, 448585456}, '-0x123456789ABCDEF0')
+ eval_eq('integer', { 1, 3, 1684581617, 448585456}, ' 0xF23456789ABCDEF0')
+ eval_eq('integer', {-1, 1, 1684581617, 448585456}, '-0x723456789ABCDEF0')
+ eval_eq('integer', { 1, 0, 0, 0x100}, '0x100')
+ eval_eq('integer', {-1, 0, 0, 0x100}, '-0x100')
+
+ eval_eq('integer', ('a'):byte(), '\'a\'')
+ eval_eq('integer', 0xAB, '\'«\'')
+ end)
+
+ it('correctly loads constants', function()
+ eval_eq('boolean', 1, 'TRUE')
+ eval_eq('boolean', 0, 'FALSE')
+ eval_eq('nil', 0, 'NIL')
+ eval_eq('nil', 0, 'NIL', '{"NIL": 1, "nan": 2, "T": 3}')
+ eval_eq('float', -(1.0/0.0-1.0/0.0), 'nan',
+ '{"NIL": "1", "nan": "2", "T": "3"}')
+ eval_eq('integer', 3, 'T', '{"NIL": "1", "nan": "2", "T": "3"}')
+ eval_eq('integer', {1, 0, 0, 0}, 'T',
+ ('{"NIL": "1", "nan": "2", "T": \'%s\'}'):format(
+ sp('integer', '[1, 0, 0, 0]')))
+ end)
+
+ it('correctly loads maps', function()
+ eval_eq('map', {}, '{}')
+ eval_eq('map', {{{_TYPE={}, _VAL={{1, 2}}}, {_TYPE={}, _VAL={{3, 4}}}}},
+ '{{1: 2}: {3: 4}}')
+ eval_eq('map', {{{_TYPE={}, _VAL={{1, 2}}}, {_TYPE={}, _VAL={{3, 4}}}},
+ {1, 2}},
+ '{{1: 2}: {3: 4}, 1: 2}')
+ end)
+
+ it('correctly loads arrays', function()
+ eval_eq('array', {}, '[]')
+ eval_eq('array', {1}, '[1]')
+ eval_eq('array', {{_TYPE={}, _VAL=1}}, '[TRUE]')
+ eval_eq('array', {{{_TYPE={}, _VAL={{1, 2}}}}, {_TYPE={}, _VAL={{3, 4}}}},
+ '[[{1: 2}], {3: 4}]')
+ end)
+
+ it('errors out when needed', function()
+ eq('empty:Parsed string is empty',
+ exc_exec('call msgpack#eval("", {})'))
+ eq('unknown:Invalid non-space character: ^',
+ exc_exec('call msgpack#eval("^", {})'))
+ eq('char-invalid:Invalid integer character literal format: \'\'',
+ exc_exec('call msgpack#eval("\'\'", {})'))
+ eq('char-invalid:Invalid integer character literal format: \'ab\'',
+ exc_exec('call msgpack#eval("\'ab\'", {})'))
+ eq('char-invalid:Invalid integer character literal format: \'',
+ exc_exec('call msgpack#eval("\'", {})'))
+ eq('"-invalid:Invalid string: "',
+ exc_exec('call msgpack#eval("\\"", {})'))
+ eq('"-invalid:Invalid string: ="',
+ exc_exec('call msgpack#eval("=\\"", {})'))
+ eq('"-invalid:Invalid string: +(0)"',
+ exc_exec('call msgpack#eval("+(0)\\"", {})'))
+ eq('0.-nodigits:Decimal dot must be followed by digit(s): .e1',
+ exc_exec('call msgpack#eval("0.e1", {})'))
+ eq('0x-long:Must have at most 16 hex digits: FEDCBA98765432100',
+ exc_exec('call msgpack#eval("0xFEDCBA98765432100", {})'))
+ eq('0x-empty:Must have number after 0x: ',
+ exc_exec('call msgpack#eval("0x", {})'))
+ eq('name-unknown:Unknown name FOO: FOO',
+ exc_exec('call msgpack#eval("FOO", {})'))
+ end)
+ end)
+end)