mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-01-24 06:40:05 +08:00
333 lines
7.9 KiB
VimL
Vendored
333 lines
7.9 KiB
VimL
Vendored
let s:save_cpo = &cpo
|
|
set cpo&vim
|
|
|
|
"
|
|
" public api
|
|
"
|
|
function! neobundle#TOML#parse(text) abort
|
|
let input = {
|
|
\ 'text': a:text,
|
|
\ 'p': 0,
|
|
\ 'length': strlen(a:text),
|
|
\}
|
|
return s:_parse(input)
|
|
endfunction
|
|
|
|
function! neobundle#TOML#parse_file(filename) abort
|
|
if !filereadable(a:filename)
|
|
throw printf("vital: Text.TOML: No such file `%s'.", a:filename)
|
|
endif
|
|
|
|
let text = join(readfile(a:filename), "\n")
|
|
" fileencoding is always utf8
|
|
return neobundle#TOML#parse(iconv(text, 'utf8', &encoding))
|
|
endfunction
|
|
|
|
"
|
|
" private api
|
|
"
|
|
" work around: '[^\r\n]*' doesn't work well in old-vim, but "[^\r\n]*" works well
|
|
let s:skip_pattern = '\C^\%(\_s\+\|' . "#[^\r\n]*" . '\)'
|
|
let s:table_name_pattern = '\%([^ [:tab:]#.[\]=]\+\)'
|
|
let s:table_key_pattern = s:table_name_pattern
|
|
|
|
function! s:_skip(input) abort
|
|
while s:_match(a:input, '\%(\_s\|#\)')
|
|
let a:input.p = matchend(a:input.text, s:skip_pattern, a:input.p)
|
|
endwhile
|
|
endfunction
|
|
|
|
function! s:_consume(input, pattern) abort
|
|
call s:_skip(a:input)
|
|
let end = matchend(a:input.text, '\C^' . a:pattern, a:input.p)
|
|
|
|
if end == -1
|
|
call s:_error(a:input)
|
|
elseif end == a:input.p
|
|
return ''
|
|
endif
|
|
|
|
let matched = strpart(a:input.text, a:input.p, end - a:input.p)
|
|
let a:input.p = end
|
|
return matched
|
|
endfunction
|
|
|
|
function! s:_match(input, pattern) abort
|
|
return match(a:input.text, '\C^' . a:pattern, a:input.p) != -1
|
|
endfunction
|
|
|
|
function! s:_eof(input) abort
|
|
return a:input.p >= a:input.length
|
|
endfunction
|
|
|
|
function! s:_error(input) abort
|
|
let buf = []
|
|
let offset = 0
|
|
while (a:input.p + offset) < a:input.length && a:input.text[a:input.p + offset] !~# "[\r\n]"
|
|
let buf += [a:input.text[a:input.p + offset]]
|
|
let offset += 1
|
|
endwhile
|
|
|
|
throw printf("vital: Text.TOML: Illegal toml format at `%s'.", join(buf, ''))
|
|
endfunction
|
|
|
|
function! s:_parse(input) abort
|
|
let data = {}
|
|
|
|
call s:_skip(a:input)
|
|
while !s:_eof(a:input)
|
|
if s:_match(a:input, '[^ [:tab:]#.[\]]')
|
|
let key = s:_key(a:input)
|
|
call s:_equals(a:input)
|
|
let value = s:_value(a:input)
|
|
|
|
call s:_put_dict(data, key, value)
|
|
|
|
unlet value
|
|
elseif s:_match(a:input, '\[\[')
|
|
let [key, value] = s:_array_of_tables(a:input)
|
|
|
|
call s:_put_array(data, key, value)
|
|
|
|
unlet value
|
|
elseif s:_match(a:input, '\[')
|
|
let [key, value] = s:_table(a:input)
|
|
|
|
call s:_put_dict(data, key, value)
|
|
|
|
unlet value
|
|
else
|
|
call s:_error(a:input)
|
|
endif
|
|
call s:_skip(a:input)
|
|
endwhile
|
|
|
|
return data
|
|
endfunction
|
|
|
|
function! s:_key(input) abort
|
|
let s = s:_consume(a:input, s:table_key_pattern)
|
|
return s
|
|
endfunction
|
|
|
|
function! s:_equals(input) abort
|
|
call s:_consume(a:input, '=')
|
|
return '='
|
|
endfunction
|
|
|
|
function! s:_value(input) abort
|
|
call s:_skip(a:input)
|
|
|
|
if s:_match(a:input, '"\{3}')
|
|
return s:_multiline_basic_string(a:input)
|
|
elseif s:_match(a:input, '"\{1}')
|
|
return s:_basic_string(a:input)
|
|
elseif s:_match(a:input, "'\\{3}")
|
|
return s:_multiline_literal(a:input)
|
|
elseif s:_match(a:input, "'\\{1}")
|
|
return s:_literal(a:input)
|
|
elseif s:_match(a:input, '\[')
|
|
return s:_array(a:input)
|
|
elseif s:_match(a:input, '\%(true\|false\)')
|
|
return s:_boolean(a:input)
|
|
elseif s:_match(a:input, '\d\{4}-')
|
|
return s:_datetime(a:input)
|
|
elseif s:_match(a:input, '[+-]\?\%(\d\+\.\d\|\d\+\%(\.\d\+\)\?[eE]\)')
|
|
return s:_float(a:input)
|
|
else
|
|
return s:_integer(a:input)
|
|
endif
|
|
endfunction
|
|
|
|
"
|
|
" String
|
|
"
|
|
function! s:_basic_string(input) abort
|
|
let s = s:_consume(a:input, '"\%(\\"\|[^"]\)*"')
|
|
let s = s[1 : -2]
|
|
return s:_unescape(s)
|
|
endfunction
|
|
|
|
function! s:_multiline_basic_string(input) abort
|
|
let s = s:_consume(a:input, '"\{3}\_.\{-}"\{3}')
|
|
let s = s[3 : -4]
|
|
let s = substitute(s, "^\n", '', '')
|
|
let s = substitute(s, '\\' . "\n" . '\_s*', '', 'g')
|
|
return s:_unescape(s)
|
|
endfunction
|
|
|
|
function! s:_literal(input) abort
|
|
let s = s:_consume(a:input, "'[^']*'")
|
|
return s[1 : -2]
|
|
endfunction
|
|
|
|
function! s:_multiline_literal(input) abort
|
|
let s = s:_consume(a:input, "'\\{3}.\\{-}'\\{3}")
|
|
let s = s[3 : -4]
|
|
let s = substitute(s, "^\n", '', '')
|
|
return s
|
|
endfunction
|
|
|
|
"
|
|
" Integer
|
|
"
|
|
function! s:_integer(input) abort
|
|
let s = s:_consume(a:input, '[+-]\?\d\+')
|
|
return str2nr(s)
|
|
endfunction
|
|
|
|
"
|
|
" Float
|
|
"
|
|
function! s:_float(input) abort
|
|
if s:_match(a:input, '[+-]\?[0-9.]\+[eE][+-]\?\d\+')
|
|
return s:_exponent(a:input)
|
|
else
|
|
return s:_fractional(a:input)
|
|
endif
|
|
endfunction
|
|
|
|
function! s:_fractional(input) abort
|
|
let s = s:_consume(a:input, '[+-]\?[0-9.]\+')
|
|
return str2float(s)
|
|
endfunction
|
|
|
|
function! s:_exponent(input) abort
|
|
let s = s:_consume(a:input, '[+-]\?[0-9.]\+[eE][+-]\?\d\+')
|
|
return str2float(s)
|
|
endfunction
|
|
|
|
"
|
|
" Boolean
|
|
"
|
|
function! s:_boolean(input) abort
|
|
let s = s:_consume(a:input, '\%(true\|false\)')
|
|
return (s ==# 'true') ? 1 : 0
|
|
endfunction
|
|
|
|
"
|
|
" Datetime
|
|
"
|
|
function! s:_datetime(input) abort
|
|
let s = s:_consume(a:input, '\d\{4}-\d\{2}-\d\{2}T\d\{2}:\d\{2}:\d\{2}\%(Z\|-\?\d\{2}:\d\{2}\|\.\d\+-\d\{2}:\d\{2}\)')
|
|
return s
|
|
endfunction
|
|
|
|
"
|
|
" Array
|
|
"
|
|
function! s:_array(input) abort
|
|
let ary = []
|
|
let _ = s:_consume(a:input, '\[')
|
|
call s:_skip(a:input)
|
|
while !s:_eof(a:input) && !s:_match(a:input, '\]')
|
|
let ary += [s:_value(a:input)]
|
|
call s:_consume(a:input, ',\?')
|
|
call s:_skip(a:input)
|
|
endwhile
|
|
let _ = s:_consume(a:input, '\]')
|
|
return ary
|
|
endfunction
|
|
|
|
"
|
|
" Table
|
|
"
|
|
function! s:_table(input) abort
|
|
let tbl = {}
|
|
let name = s:_consume(a:input, '\[\s*' . s:table_name_pattern . '\%(\s*\.\s*' . s:table_name_pattern . '\)*\s*\]')
|
|
let name = name[1 : -2]
|
|
call s:_skip(a:input)
|
|
" while !s:_eof(a:input) && !s:_match(a:input, '\[\{1,2}[a-zA-Z0-9.]\+\]\{1,2}')
|
|
while !s:_eof(a:input) && !s:_match(a:input, '\[')
|
|
let key = s:_key(a:input)
|
|
call s:_equals(a:input)
|
|
let value = s:_value(a:input)
|
|
|
|
let tbl[key] = value
|
|
|
|
unlet value
|
|
call s:_skip(a:input)
|
|
endwhile
|
|
return [name, tbl]
|
|
endfunction
|
|
|
|
"
|
|
" Array of tables
|
|
"
|
|
function! s:_array_of_tables(input) abort
|
|
let tbl = {}
|
|
let name = s:_consume(a:input, '\[\[\s*' . s:table_name_pattern . '\%(\s*\.\s*' . s:table_name_pattern . '\)*\s*\]\]')
|
|
let name = name[2 : -3]
|
|
call s:_skip(a:input)
|
|
" while !s:_eof(a:input) && !s:_match(a:input, '\[\{1,2}[a-zA-Z0-9.]\+\]\{1,2}')
|
|
while !s:_eof(a:input) && !s:_match(a:input, '\[')
|
|
let key = s:_key(a:input)
|
|
call s:_equals(a:input)
|
|
let value = s:_value(a:input)
|
|
|
|
let tbl[key] = value
|
|
|
|
unlet value
|
|
call s:_skip(a:input)
|
|
endwhile
|
|
return [name, [tbl]]
|
|
endfunction
|
|
|
|
function! s:_unescape(text) abort
|
|
let text = a:text
|
|
let text = substitute(text, '\\"', '"', 'g')
|
|
let text = substitute(text, '\\b', "\b", 'g')
|
|
let text = substitute(text, '\\t', "\t", 'g')
|
|
let text = substitute(text, '\\n', "\n", 'g')
|
|
let text = substitute(text, '\\f', "\f", 'g')
|
|
let text = substitute(text, '\\r', "\r", 'g')
|
|
let text = substitute(text, '\\/', "/", 'g')
|
|
let text = substitute(text, '\\\\', '\', 'g')
|
|
let text = substitute(text, '\C\\u\(\x\{4}\)', '\=s:_nr2char("0x" . submatch(1))', 'g')
|
|
let text = substitute(text, '\C\\U\(\x\{8}\)', '\=s:_nr2char("0x" . submatch(1))', 'g')
|
|
return text
|
|
endfunction
|
|
|
|
function! s:_nr2char(nr) abort
|
|
return iconv(nr2char(a:nr), &encoding, 'utf8')
|
|
endfunction
|
|
|
|
function! s:_put_dict(dict, key, value) abort
|
|
let keys = split(a:key, '\.')
|
|
|
|
let ref = a:dict
|
|
for key in keys[ : -2]
|
|
if has_key(ref, key) && type(ref[key]) == type({})
|
|
let ref = ref[key]
|
|
elseif has_key(ref, key) && type(ref[key]) == type([])
|
|
let ref = ref[key][-1]
|
|
else
|
|
let ref[key] = {}
|
|
let ref = ref[key]
|
|
endif
|
|
endfor
|
|
|
|
let ref[keys[-1]] = a:value
|
|
endfunction
|
|
|
|
function! s:_put_array(dict, key, value) abort
|
|
let keys = split(a:key, '\.')
|
|
|
|
let ref = a:dict
|
|
for key in keys[ : -2]
|
|
let ref[key] = get(ref, key, {})
|
|
|
|
if type(ref[key]) == type([])
|
|
let ref = ref[key][-1]
|
|
else
|
|
let ref = ref[key]
|
|
endif
|
|
endfor
|
|
|
|
let ref[keys[-1]] = get(ref, keys[-1], []) + a:value
|
|
endfunction
|
|
|
|
let &cpo = s:save_cpo
|
|
unlet s:save_cpo
|
|
" vim:set et ts=2 sts=2 sw=2 tw=0:
|