Skip to content

Commit e28db14

Browse files
committed
Web.JSON: Add 'allow_nan' setting to encode() and decode().
1 parent 28e7f51 commit e28db14

File tree

3 files changed

+72
-0
lines changed

3 files changed

+72
-0
lines changed

autoload/vital/__vital__/Web/JSON.vim

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
let s:save_cpo = &cpo
22
set cpo&vim
33

4+
let s:float_constants = {
5+
\ 'nan': 'NaN',
6+
\ '-nan': 'NaN',
7+
\ 'inf': 'Infinity',
8+
\ '-inf': '-Infinity',
9+
\ }
10+
let s:float_nan = 0.0 / 0
11+
let s:float_inf = 1.0 / 0
12+
lockvar s:float_constants s:float_nan s:float_inf
13+
414
function! s:_true() abort
515
return 1
616
endfunction
@@ -51,14 +61,20 @@ endfunction
5161
" @vimlint(EVL102, 1, l:null)
5262
" @vimlint(EVL102, 1, l:true)
5363
" @vimlint(EVL102, 1, l:false)
64+
" @vimlint(EVL102, 1, l:NaN)
65+
" @vimlint(EVL102, 1, l:Infinity)
5466
function! s:decode(json, ...) abort
5567
let settings = extend({
5668
\ 'use_token': 0,
69+
\ 'allow_nan': 1,
5770
\}, get(a:000, 0, {}))
5871
let json = iconv(a:json, 'utf-8', &encoding)
5972
let json = join(split(json, "\n"), '')
6073
let json = substitute(json, '\\u34;', '\\"', 'g')
6174
let json = substitute(json, '\\u\(\x\x\x\x\)', '\=s:string.nr2enc_char("0x".submatch(1))', 'g')
75+
if settings.allow_nan
76+
let [NaN,Infinity] = [s:float_nan,s:float_inf]
77+
endif
6278
if settings.use_token
6379
let prefix = '__Web.JSON__'
6480
while stridx(json, prefix) != -1
@@ -74,10 +90,13 @@ endfunction
7490
" @vimlint(EVL102, 0, l:null)
7591
" @vimlint(EVL102, 0, l:true)
7692
" @vimlint(EVL102, 0, l:false)
93+
" @vimlint(EVL102, 0, l:NaN)
94+
" @vimlint(EVL102, 0, l:Infinity)
7795

7896
function! s:encode(val, ...) abort
7997
let settings = extend({
8098
\ 'indent': 0,
99+
\ 'allow_nan': 1,
81100
\}, get(a:000, 0, {})
82101
\)
83102
let t = type(a:val)
@@ -104,6 +123,14 @@ function! s:encode(val, ...) abort
104123
return s:_encode_list(a:val, settings)
105124
elseif t == 4
106125
return s:_encode_dict(a:val, settings)
126+
elseif t == 5
127+
let val = string(a:val)
128+
if settings.allow_nan
129+
let val = get(s:float_constants, val, val)
130+
elseif has_key(s:float_constants, val)
131+
throw 'vital: Web.JSON: Invalid float value: ' . val
132+
endif
133+
return val
107134
else
108135
return string(a:val)
109136
endif

doc/vital/Web/JSON.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,18 @@ encode({object}[, {settings}]) *Vital.Web.JSON.encode()*
6565
" "a": 0,
6666
" "b": 1
6767
" }'
68+
<
69+
'allow_nan'
70+
If 'allows_nan' is 0, it will raise an exception when serializing
71+
out of range float values (nan, inf, -inf).
72+
Otherwise 'NaN', 'Infinity' or '-Infinity' are used to represent these.
73+
The default value is 1.
74+
Note that NaN and Infinity are not the JSON standard.
75+
>
76+
echo s:JSON.encode([0.0/0, 1.0/0, -1.0/0])
77+
" => [NaN,Infinity,-Infinity]
78+
echo s:JSON.encode([0.0/0], {'allow_nan': 0})
79+
" => vital: Web.JSON: Invalid float value: nan
6880
<
6981
decode({json}[, {settings}]) *Vital.Web.JSON.decode()*
7082
Decode a JSON string into an object that vim can treat.
@@ -81,6 +93,17 @@ decode({json}[, {settings}]) *Vital.Web.JSON.decode()*
8193
echo s:JSON.decode('[true, false, null]', {'use_token': 1})
8294
" => [s:JSON.true, s:JSON.false, s:JSON.null]
8395
<
96+
'allow_nan'
97+
If 'allows_nan' is 0, it will raise an exception when deserializing
98+
float constants ('NaN', 'Infinity', '-Infinity').
99+
Otherwise nan, inf or -inf are used to represent these.
100+
The default value is 1.
101+
>
102+
echo s:JSON.decode('[NaN, Infinity, -Infinity]')
103+
" => [nan, inf, -inf]
104+
echo s:JSON.decode('[NaN]', {'allow_nan': 0})
105+
" => E121: Undefined variable: NaN
106+
<
84107

85108
=============================================================================
86109
vim:tw=78:fo=tcq2mM:ts=8:ft=help:norl

test/Web/JSON.vimspec

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,17 @@ Describe Web.JSON
7171
Assert Equals(JSON.decode('false', s), JSON.false)
7272
Assert Equals(JSON.decode('null', s), JSON.null)
7373
End
74+
75+
It decodes special floats (NaN/Infinity/-Infinity)
76+
Assert Equals(string(JSON.decode('NaN')), 'nan')
77+
Assert Equals(string(JSON.decode('Infinity')), 'inf')
78+
Assert Equals(string(JSON.decode('-Infinity')), '-inf')
79+
80+
let s = { 'allow_nan': 0 }
81+
Throws /^Vim(\w\+):E121:/ JSON.decode('NaN', s)
82+
Throws /^Vim(\w\+):E121:/ JSON.decode('Infinity', s)
83+
Throws /^Vim(\w\+):E121:/ JSON.decode('-Infinity', s)
84+
End
7485
End
7586

7687
Describe .encode()
@@ -213,5 +224,16 @@ Describe Web.JSON
213224
Assert Equals(JSON.encode(JSON.false), 'false')
214225
Assert Equals(JSON.encode(JSON.null), 'null')
215226
End
227+
228+
It encodes special floats (NaN/Infinity/-Infinity)
229+
Assert Equals(JSON.encode(0.0/0), 'NaN')
230+
Assert Equals(JSON.encode(1.0/0), 'Infinity')
231+
Assert Equals(JSON.encode(-1.0/0), '-Infinity')
232+
233+
let s = { 'allow_nan': 0 }
234+
Throws /^vital: Web.JSON:/ JSON.encode(0.0/0, s)
235+
Throws /^vital: Web.JSON:/ JSON.encode(1.0/0, s)
236+
Throws /^vital: Web.JSON:/ JSON.encode(-1.0/0, s)
237+
End
216238
End
217239
End

0 commit comments

Comments
 (0)