Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
J
json
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Chen Yisong
json
Commits
22b9a301
Commit
22b9a301
authored
Feb 14, 2017
by
Niels Lohmann
Committed by
GitHub
Feb 14, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #450 from nlohmann/TurpentineDistillery-feature/locale_independent_str_to_num
TurpentineDistillery feature/locale independent str to num
parents
c95ff863
265c5b52
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
597 additions
and
327 deletions
+597
-327
Makefile
Makefile
+1
-1
json.hpp
src/json.hpp
+337
-186
json.hpp.re2c
src/json.hpp.re2c
+200
-127
unit-class_lexer.cpp
test/src/unit-class_lexer.cpp
+25
-11
unit-class_parser.cpp
test/src/unit-class_parser.cpp
+7
-1
unit-regression.cpp
test/src/unit-regression.cpp
+27
-1
No files found.
Makefile
View file @
22b9a301
...
@@ -94,7 +94,7 @@ cppcheck:
...
@@ -94,7 +94,7 @@ cppcheck:
# run clang sanitize (we are overrding the CXXFLAGS provided by travis in order to use gcc's libstdc++)
# run clang sanitize (we are overrding the CXXFLAGS provided by travis in order to use gcc's libstdc++)
clang_sanitize
:
clean
clang_sanitize
:
clean
CXX
=
clang++
CXXFLAGS
=
"-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer"
$(MAKE)
CXX
=
clang++
CXXFLAGS
=
"-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer"
$(MAKE)
check
##########################################################################
##########################################################################
...
...
src/json.hpp
View file @
22b9a301
...
@@ -9444,7 +9444,9 @@ class basic_json
...
@@ -9444,7 +9444,9 @@ class basic_json
literal_false
,
///< the `false` literal
literal_false
,
///< the `false` literal
literal_null
,
///< the `null` literal
literal_null
,
///< the `null` literal
value_string
,
///< a string -- use get_string() for actual value
value_string
,
///< a string -- use get_string() for actual value
value_number
,
///< a number -- use get_number() for actual value
value_unsigned
,
///< an unsigned integer -- use get_number() for actual value
value_integer
,
///< a signed integer -- use get_number() for actual value
value_float
,
///< an floating point number -- use get_number() for actual value
begin_array
,
///< the character for array begin `[`
begin_array
,
///< the character for array begin `[`
begin_object
,
///< the character for object begin `{`
begin_object
,
///< the character for object begin `{`
end_array
,
///< the character for array end `]`
end_array
,
///< the character for array end `]`
...
@@ -9596,7 +9598,9 @@ class basic_json
...
@@ -9596,7 +9598,9 @@ class basic_json
return
"null literal"
;
return
"null literal"
;
case
token_type
:
:
value_string
:
case
token_type
:
:
value_string
:
return
"string literal"
;
return
"string literal"
;
case
token_type
:
:
value_number
:
case
lexer
:
:
token_type
::
value_unsigned
:
case
lexer
:
:
token_type
::
value_integer
:
case
lexer
:
:
token_type
::
value_float
:
return
"number literal"
;
return
"number literal"
;
case
token_type
:
:
begin_array
:
case
token_type
:
:
begin_array
:
return
"'['"
;
return
"'['"
;
...
@@ -9869,11 +9873,11 @@ basic_json_parser_12:
...
@@ -9869,11 +9873,11 @@ basic_json_parser_12:
}
}
if
(
yych
<=
'0'
)
if
(
yych
<=
'0'
)
{
{
goto
basic_json_parser_
1
3
;
goto
basic_json_parser_
4
3
;
}
}
if
(
yych
<=
'9'
)
if
(
yych
<=
'9'
)
{
{
goto
basic_json_parser_
1
5
;
goto
basic_json_parser_
4
5
;
}
}
goto
basic_json_parser_5
;
goto
basic_json_parser_5
;
basic_json_parser_13
:
basic_json_parser_13
:
...
@@ -9883,23 +9887,23 @@ basic_json_parser_13:
...
@@ -9883,23 +9887,23 @@ basic_json_parser_13:
{
{
if
(
yych
==
'.'
)
if
(
yych
==
'.'
)
{
{
goto
basic_json_parser_4
3
;
goto
basic_json_parser_4
7
;
}
}
}
}
else
else
{
{
if
(
yych
<=
'E'
)
if
(
yych
<=
'E'
)
{
{
goto
basic_json_parser_4
4
;
goto
basic_json_parser_4
8
;
}
}
if
(
yych
==
'e'
)
if
(
yych
==
'e'
)
{
{
goto
basic_json_parser_4
4
;
goto
basic_json_parser_4
8
;
}
}
}
}
basic_json_parser_14
:
basic_json_parser_14
:
{
{
last_token_type
=
token_type
::
value_
number
;
last_token_type
=
token_type
::
value_
unsigned
;
break
;
break
;
}
}
basic_json_parser_15
:
basic_json_parser_15
:
...
@@ -9918,7 +9922,7 @@ basic_json_parser_15:
...
@@ -9918,7 +9922,7 @@ basic_json_parser_15:
{
{
if
(
yych
==
'.'
)
if
(
yych
==
'.'
)
{
{
goto
basic_json_parser_4
3
;
goto
basic_json_parser_4
7
;
}
}
goto
basic_json_parser_14
;
goto
basic_json_parser_14
;
}
}
...
@@ -9926,11 +9930,11 @@ basic_json_parser_15:
...
@@ -9926,11 +9930,11 @@ basic_json_parser_15:
{
{
if
(
yych
<=
'E'
)
if
(
yych
<=
'E'
)
{
{
goto
basic_json_parser_4
4
;
goto
basic_json_parser_4
8
;
}
}
if
(
yych
==
'e'
)
if
(
yych
==
'e'
)
{
{
goto
basic_json_parser_4
4
;
goto
basic_json_parser_4
8
;
}
}
goto
basic_json_parser_14
;
goto
basic_json_parser_14
;
}
}
...
@@ -9957,7 +9961,7 @@ basic_json_parser_23:
...
@@ -9957,7 +9961,7 @@ basic_json_parser_23:
yych
=
*
(
m_marker
=
++
m_cursor
);
yych
=
*
(
m_marker
=
++
m_cursor
);
if
(
yych
==
'a'
)
if
(
yych
==
'a'
)
{
{
goto
basic_json_parser_4
5
;
goto
basic_json_parser_4
9
;
}
}
goto
basic_json_parser_5
;
goto
basic_json_parser_5
;
basic_json_parser_24
:
basic_json_parser_24
:
...
@@ -9965,7 +9969,7 @@ basic_json_parser_24:
...
@@ -9965,7 +9969,7 @@ basic_json_parser_24:
yych
=
*
(
m_marker
=
++
m_cursor
);
yych
=
*
(
m_marker
=
++
m_cursor
);
if
(
yych
==
'u'
)
if
(
yych
==
'u'
)
{
{
goto
basic_json_parser_
46
;
goto
basic_json_parser_
50
;
}
}
goto
basic_json_parser_5
;
goto
basic_json_parser_5
;
basic_json_parser_25
:
basic_json_parser_25
:
...
@@ -9973,7 +9977,7 @@ basic_json_parser_25:
...
@@ -9973,7 +9977,7 @@ basic_json_parser_25:
yych
=
*
(
m_marker
=
++
m_cursor
);
yych
=
*
(
m_marker
=
++
m_cursor
);
if
(
yych
==
'r'
)
if
(
yych
==
'r'
)
{
{
goto
basic_json_parser_
47
;
goto
basic_json_parser_
51
;
}
}
goto
basic_json_parser_5
;
goto
basic_json_parser_5
;
basic_json_parser_26
:
basic_json_parser_26
:
...
@@ -10055,6 +10059,8 @@ basic_json_parser_31:
...
@@ -10055,6 +10059,8 @@ basic_json_parser_31:
}
}
basic_json_parser_32
:
basic_json_parser_32
:
m_cursor
=
m_marker
;
m_cursor
=
m_marker
;
if
(
yyaccept
<=
1
)
{
if
(
yyaccept
==
0
)
if
(
yyaccept
==
0
)
{
{
goto
basic_json_parser_5
;
goto
basic_json_parser_5
;
...
@@ -10063,6 +10069,18 @@ basic_json_parser_32:
...
@@ -10063,6 +10069,18 @@ basic_json_parser_32:
{
{
goto
basic_json_parser_14
;
goto
basic_json_parser_14
;
}
}
}
else
{
if
(
yyaccept
==
2
)
{
goto
basic_json_parser_44
;
}
else
{
goto
basic_json_parser_55
;
}
}
basic_json_parser_33
:
basic_json_parser_33
:
++
m_cursor
;
++
m_cursor
;
{
{
...
@@ -10142,7 +10160,7 @@ basic_json_parser_35:
...
@@ -10142,7 +10160,7 @@ basic_json_parser_35:
}
}
if
(
yych
<=
'u'
)
if
(
yych
<=
'u'
)
{
{
goto
basic_json_parser_
48
;
goto
basic_json_parser_
52
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
}
}
...
@@ -10261,6 +10279,71 @@ basic_json_parser_42:
...
@@ -10261,6 +10279,71 @@ basic_json_parser_42:
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
basic_json_parser_43
:
basic_json_parser_43
:
yyaccept
=
2
;
yych
=
*
(
m_marker
=
++
m_cursor
);
if
(
yych
<=
'D'
)
{
if
(
yych
==
'.'
)
{
goto
basic_json_parser_47
;
}
}
else
{
if
(
yych
<=
'E'
)
{
goto
basic_json_parser_48
;
}
if
(
yych
==
'e'
)
{
goto
basic_json_parser_48
;
}
}
basic_json_parser_44
:
{
last_token_type
=
token_type
::
value_integer
;
break
;
}
basic_json_parser_45
:
yyaccept
=
2
;
m_marker
=
++
m_cursor
;
if
((
m_limit
-
m_cursor
)
<
3
)
{
fill_line_buffer
(
3
);
// LCOV_EXCL_LINE
}
yych
=
*
m_cursor
;
if
(
yych
<=
'9'
)
{
if
(
yych
==
'.'
)
{
goto
basic_json_parser_47
;
}
if
(
yych
<=
'/'
)
{
goto
basic_json_parser_44
;
}
goto
basic_json_parser_45
;
}
else
{
if
(
yych
<=
'E'
)
{
if
(
yych
<=
'D'
)
{
goto
basic_json_parser_44
;
}
goto
basic_json_parser_48
;
}
else
{
if
(
yych
==
'e'
)
{
goto
basic_json_parser_48
;
}
goto
basic_json_parser_44
;
}
}
basic_json_parser_47
:
yych
=
*++
m_cursor
;
yych
=
*++
m_cursor
;
if
(
yych
<=
'/'
)
if
(
yych
<=
'/'
)
{
{
...
@@ -10268,16 +10351,16 @@ basic_json_parser_43:
...
@@ -10268,16 +10351,16 @@ basic_json_parser_43:
}
}
if
(
yych
<=
'9'
)
if
(
yych
<=
'9'
)
{
{
goto
basic_json_parser_
49
;
goto
basic_json_parser_
53
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
basic_json_parser_4
4
:
basic_json_parser_4
8
:
yych
=
*++
m_cursor
;
yych
=
*++
m_cursor
;
if
(
yych
<=
','
)
if
(
yych
<=
','
)
{
{
if
(
yych
==
'+'
)
if
(
yych
==
'+'
)
{
{
goto
basic_json_parser_5
1
;
goto
basic_json_parser_5
6
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
}
}
...
@@ -10285,7 +10368,7 @@ basic_json_parser_44:
...
@@ -10285,7 +10368,7 @@ basic_json_parser_44:
{
{
if
(
yych
<=
'-'
)
if
(
yych
<=
'-'
)
{
{
goto
basic_json_parser_5
1
;
goto
basic_json_parser_5
6
;
}
}
if
(
yych
<=
'/'
)
if
(
yych
<=
'/'
)
{
{
...
@@ -10293,32 +10376,32 @@ basic_json_parser_44:
...
@@ -10293,32 +10376,32 @@ basic_json_parser_44:
}
}
if
(
yych
<=
'9'
)
if
(
yych
<=
'9'
)
{
{
goto
basic_json_parser_5
2
;
goto
basic_json_parser_5
7
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
}
}
basic_json_parser_4
5
:
basic_json_parser_4
9
:
yych
=
*++
m_cursor
;
yych
=
*++
m_cursor
;
if
(
yych
==
'l'
)
if
(
yych
==
'l'
)
{
{
goto
basic_json_parser_5
4
;
goto
basic_json_parser_5
9
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
basic_json_parser_
46
:
basic_json_parser_
50
:
yych
=
*++
m_cursor
;
yych
=
*++
m_cursor
;
if
(
yych
==
'l'
)
if
(
yych
==
'l'
)
{
{
goto
basic_json_parser_
55
;
goto
basic_json_parser_
60
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
basic_json_parser_
47
:
basic_json_parser_
51
:
yych
=
*++
m_cursor
;
yych
=
*++
m_cursor
;
if
(
yych
==
'u'
)
if
(
yych
==
'u'
)
{
{
goto
basic_json_parser_
56
;
goto
basic_json_parser_
61
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
basic_json_parser_
48
:
basic_json_parser_
52
:
++
m_cursor
;
++
m_cursor
;
if
(
m_limit
<=
m_cursor
)
if
(
m_limit
<=
m_cursor
)
{
{
...
@@ -10333,7 +10416,7 @@ basic_json_parser_48:
...
@@ -10333,7 +10416,7 @@ basic_json_parser_48:
}
}
if
(
yych
<=
'9'
)
if
(
yych
<=
'9'
)
{
{
goto
basic_json_parser_
57
;
goto
basic_json_parser_
62
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
}
}
...
@@ -10341,7 +10424,7 @@ basic_json_parser_48:
...
@@ -10341,7 +10424,7 @@ basic_json_parser_48:
{
{
if
(
yych
<=
'F'
)
if
(
yych
<=
'F'
)
{
{
goto
basic_json_parser_
57
;
goto
basic_json_parser_
62
;
}
}
if
(
yych
<=
'`'
)
if
(
yych
<=
'`'
)
{
{
...
@@ -10349,12 +10432,12 @@ basic_json_parser_48:
...
@@ -10349,12 +10432,12 @@ basic_json_parser_48:
}
}
if
(
yych
<=
'f'
)
if
(
yych
<=
'f'
)
{
{
goto
basic_json_parser_
57
;
goto
basic_json_parser_
62
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
}
}
basic_json_parser_
49
:
basic_json_parser_
53
:
yyaccept
=
1
;
yyaccept
=
3
;
m_marker
=
++
m_cursor
;
m_marker
=
++
m_cursor
;
if
((
m_limit
-
m_cursor
)
<
3
)
if
((
m_limit
-
m_cursor
)
<
3
)
{
{
...
@@ -10365,27 +10448,30 @@ basic_json_parser_49:
...
@@ -10365,27 +10448,30 @@ basic_json_parser_49:
{
{
if
(
yych
<=
'/'
)
if
(
yych
<=
'/'
)
{
{
goto
basic_json_parser_
14
;
goto
basic_json_parser_
55
;
}
}
if
(
yych
<=
'9'
)
if
(
yych
<=
'9'
)
{
{
goto
basic_json_parser_
49
;
goto
basic_json_parser_
53
;
}
}
goto
basic_json_parser_14
;
}
}
else
else
{
{
if
(
yych
<=
'E'
)
if
(
yych
<=
'E'
)
{
{
goto
basic_json_parser_4
4
;
goto
basic_json_parser_4
8
;
}
}
if
(
yych
==
'e'
)
if
(
yych
==
'e'
)
{
{
goto
basic_json_parser_4
4
;
goto
basic_json_parser_4
8
;
}
}
goto
basic_json_parser_14
;
}
}
basic_json_parser_51
:
basic_json_parser_55
:
{
last_token_type
=
token_type
::
value_float
;
break
;
}
basic_json_parser_56
:
yych
=
*++
m_cursor
;
yych
=
*++
m_cursor
;
if
(
yych
<=
'/'
)
if
(
yych
<=
'/'
)
{
{
...
@@ -10395,7 +10481,7 @@ basic_json_parser_51:
...
@@ -10395,7 +10481,7 @@ basic_json_parser_51:
{
{
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
}
}
basic_json_parser_5
2
:
basic_json_parser_5
7
:
++
m_cursor
;
++
m_cursor
;
if
(
m_limit
<=
m_cursor
)
if
(
m_limit
<=
m_cursor
)
{
{
...
@@ -10404,35 +10490,35 @@ basic_json_parser_52:
...
@@ -10404,35 +10490,35 @@ basic_json_parser_52:
yych
=
*
m_cursor
;
yych
=
*
m_cursor
;
if
(
yych
<=
'/'
)
if
(
yych
<=
'/'
)
{
{
goto
basic_json_parser_
14
;
goto
basic_json_parser_
55
;
}
}
if
(
yych
<=
'9'
)
if
(
yych
<=
'9'
)
{
{
goto
basic_json_parser_5
2
;
goto
basic_json_parser_5
7
;
}
}
goto
basic_json_parser_
14
;
goto
basic_json_parser_
55
;
basic_json_parser_5
4
:
basic_json_parser_5
9
:
yych
=
*++
m_cursor
;
yych
=
*++
m_cursor
;
if
(
yych
==
's'
)
if
(
yych
==
's'
)
{
{
goto
basic_json_parser_
58
;
goto
basic_json_parser_
63
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
basic_json_parser_
55
:
basic_json_parser_
60
:
yych
=
*++
m_cursor
;
yych
=
*++
m_cursor
;
if
(
yych
==
'l'
)
if
(
yych
==
'l'
)
{
{
goto
basic_json_parser_
59
;
goto
basic_json_parser_
64
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
basic_json_parser_
56
:
basic_json_parser_
61
:
yych
=
*++
m_cursor
;
yych
=
*++
m_cursor
;
if
(
yych
==
'e'
)
if
(
yych
==
'e'
)
{
{
goto
basic_json_parser_6
1
;
goto
basic_json_parser_6
6
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
basic_json_parser_
57
:
basic_json_parser_
62
:
++
m_cursor
;
++
m_cursor
;
if
(
m_limit
<=
m_cursor
)
if
(
m_limit
<=
m_cursor
)
{
{
...
@@ -10447,7 +10533,7 @@ basic_json_parser_57:
...
@@ -10447,7 +10533,7 @@ basic_json_parser_57:
}
}
if
(
yych
<=
'9'
)
if
(
yych
<=
'9'
)
{
{
goto
basic_json_parser_6
3
;
goto
basic_json_parser_6
8
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
}
}
...
@@ -10455,7 +10541,7 @@ basic_json_parser_57:
...
@@ -10455,7 +10541,7 @@ basic_json_parser_57:
{
{
if
(
yych
<=
'F'
)
if
(
yych
<=
'F'
)
{
{
goto
basic_json_parser_6
3
;
goto
basic_json_parser_6
8
;
}
}
if
(
yych
<=
'`'
)
if
(
yych
<=
'`'
)
{
{
...
@@ -10463,30 +10549,30 @@ basic_json_parser_57:
...
@@ -10463,30 +10549,30 @@ basic_json_parser_57:
}
}
if
(
yych
<=
'f'
)
if
(
yych
<=
'f'
)
{
{
goto
basic_json_parser_6
3
;
goto
basic_json_parser_6
8
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
}
}
basic_json_parser_
58
:
basic_json_parser_
63
:
yych
=
*++
m_cursor
;
yych
=
*++
m_cursor
;
if
(
yych
==
'e'
)
if
(
yych
==
'e'
)
{
{
goto
basic_json_parser_6
4
;
goto
basic_json_parser_6
9
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
basic_json_parser_
59
:
basic_json_parser_
64
:
++
m_cursor
;
++
m_cursor
;
{
{
last_token_type
=
token_type
::
literal_null
;
last_token_type
=
token_type
::
literal_null
;
break
;
break
;
}
}
basic_json_parser_6
1
:
basic_json_parser_6
6
:
++
m_cursor
;
++
m_cursor
;
{
{
last_token_type
=
token_type
::
literal_true
;
last_token_type
=
token_type
::
literal_true
;
break
;
break
;
}
}
basic_json_parser_6
3
:
basic_json_parser_6
8
:
++
m_cursor
;
++
m_cursor
;
if
(
m_limit
<=
m_cursor
)
if
(
m_limit
<=
m_cursor
)
{
{
...
@@ -10501,7 +10587,7 @@ basic_json_parser_63:
...
@@ -10501,7 +10587,7 @@ basic_json_parser_63:
}
}
if
(
yych
<=
'9'
)
if
(
yych
<=
'9'
)
{
{
goto
basic_json_parser_
66
;
goto
basic_json_parser_
71
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
}
}
...
@@ -10509,7 +10595,7 @@ basic_json_parser_63:
...
@@ -10509,7 +10595,7 @@ basic_json_parser_63:
{
{
if
(
yych
<=
'F'
)
if
(
yych
<=
'F'
)
{
{
goto
basic_json_parser_
66
;
goto
basic_json_parser_
71
;
}
}
if
(
yych
<=
'`'
)
if
(
yych
<=
'`'
)
{
{
...
@@ -10517,17 +10603,17 @@ basic_json_parser_63:
...
@@ -10517,17 +10603,17 @@ basic_json_parser_63:
}
}
if
(
yych
<=
'f'
)
if
(
yych
<=
'f'
)
{
{
goto
basic_json_parser_
66
;
goto
basic_json_parser_
71
;
}
}
goto
basic_json_parser_32
;
goto
basic_json_parser_32
;
}
}
basic_json_parser_6
4
:
basic_json_parser_6
9
:
++
m_cursor
;
++
m_cursor
;
{
{
last_token_type
=
token_type
::
literal_false
;
last_token_type
=
token_type
::
literal_false
;
break
;
break
;
}
}
basic_json_parser_
66
:
basic_json_parser_
71
:
++
m_cursor
;
++
m_cursor
;
if
(
m_limit
<=
m_cursor
)
if
(
m_limit
<=
m_cursor
)
{
{
...
@@ -10838,186 +10924,241 @@ basic_json_parser_66:
...
@@ -10838,186 +10924,241 @@ basic_json_parser_66:
return
result
;
return
result
;
}
}
/*!
@brief parse floating point number
This function (and its overloads) serves to select the most appropriate
/*!
standard floating point number parsing function based on the type
@brief parse string into a built-in arithmetic type as if the current
supplied via the first parameter. Set this to @a
locale is POSIX.
static_cast<number_float_t*>(nullptr).
@
param[in,out] endptr receives a pointer to the first character after
@
note in floating-point case strtod may parse past the token's end -
the numbe
r
this is not an erro
r
@
return the floating point number
@
note any leading blanks are not handled
*/
*/
long
double
str_to_float_t
(
long
double
*
/* type */
,
char
**
endptr
)
const
struct
strtonum
{
{
return
std
::
strtold
(
reinterpret_cast
<
typename
string_t
::
const_pointer
>
(
m_start
),
endptr
);
public
:
}
strtonum
(
const
char
*
start
,
const
char
*
end
)
:
m_start
(
start
),
m_end
(
end
)
{}
/*!
/*!
@brief parse floating point number
@return true iff parsed successfully as number of type T
This function (and its overloads) serves to select the most appropriate
standard floating point number parsing function based on the type
supplied via the first parameter. Set this to @a
static_cast<number_float_t*>(nullptr).
@param[in,out] endptr receives a pointer to the first character after
the number
@return the floating point number
@param[in,out] val shall contain parsed value, or undefined value
if could not parse
*/
*/
double
str_to_float_t
(
double
*
/* type */
,
char
**
endptr
)
const
template
<
typename
T
,
typename
=
typename
std
::
enable_if
<
std
::
is_arithmetic
<
T
>::
value
>::
type
>
bool
to
(
T
&
val
)
const
{
{
return
std
::
strtod
(
reinterpret_cast
<
typename
string_t
::
const_pointer
>
(
m_start
),
endptr
);
return
parse
(
val
,
std
::
is_integral
<
T
>
()
);
}
}
/*!
private
:
@brief parse floating point number
const
char
*
const
m_start
=
nullptr
;
const
char
*
const
m_end
=
nullptr
;
This function (and its overloads) serves to select the most appropriate
standard floating point number parsing function based on the type
supplied via the first parameter. Set this to @a
static_cast<number_float_t*>(nullptr).
@param[in,out] endptr receives a pointer to the first character after
// floating-point conversion
the number
@return the floating point number
// overloaded wrappers for strtod/strtof/strtold
*/
// that will be called from parse<floating_point_t>
float
str_to_float_t
(
float
*
/* type */
,
char
**
endptr
)
const
static
void
strtof
(
float
&
f
,
const
char
*
str
,
char
**
endptr
)
{
{
return
std
::
strtof
(
reinterpret_cast
<
typename
string_t
::
const_pointer
>
(
m_start
)
,
endptr
);
f
=
std
::
strtof
(
str
,
endptr
);
}
}
/*!
static
void
strtof
(
double
&
f
,
const
char
*
str
,
char
**
endptr
)
@brief return number value for number tokens
{
f
=
std
::
strtod
(
str
,
endptr
);
This function translates the last token into the most appropriate
}
number type (either integer, unsigned integer or floating point),
which is passed back to the caller via the result parameter.
This function parses the integer component up to the radix point or
exponent while collecting information about the 'floating point
representation', which it stores in the result parameter. If there is
no radix point or exponent, and the number can fit into a @ref
number_integer_t or @ref number_unsigned_t then it sets the result
parameter accordingly.
If the number is a floating point number the number is then parsed
static
void
strtof
(
long
double
&
f
,
const
char
*
str
,
char
**
endptr
)
using @a std:strtod (or @a std:strtof or @a std::strtold).
{
f
=
std
::
strtold
(
str
,
endptr
);
}
@param[out] result @ref basic_json object to receive the number, or
template
<
typename
T
>
NAN if the conversion read past the current token. The latter case
bool
parse
(
T
&
value
,
/*is_integral=*/
std
::
false_type
)
const
needs to be treated by the caller function.
*/
void
get_number
(
basic_json
&
result
)
const
{
{
assert
(
m_start
!=
nullptr
);
// replace decimal separator with locale-specific version,
// when necessary; data will point to either the original
// string, or buf, or tempstr containing the fixed string.
std
::
string
tempstr
;
std
::
array
<
char
,
64
>
buf
;
const
size_t
len
=
static_cast
<
size_t
>
(
m_end
-
m_start
);
const
lexer
::
lexer_char_t
*
curptr
=
m_start
;
// lexer will reject empty numbers
assert
(
len
>
0
);
// accumulate the integer conversion result (unsigned for now)
// since dealing with strtod family of functions, we're
number_unsigned_t
value
=
0
;
// getting the decimal point char from the C locale facilities
// instead of C++'s numpunct facet of the current std::locale
const
auto
loc
=
localeconv
();
assert
(
loc
!=
nullptr
);
const
char
decimal_point_char
=
(
loc
->
decimal_point
==
nullptr
)
?
'.'
:
loc
->
decimal_point
[
0
];
// maximum absolute value of the relevant integer type
const
char
*
data
=
m_start
;
number_unsigned_t
max
;
// temporarily store the type to avoid unnecessary bitfield access
if
(
decimal_point_char
!=
'.'
)
value_t
type
;
{
const
size_t
ds_pos
=
static_cast
<
size_t
>
(
std
::
find
(
m_start
,
m_end
,
'.'
)
-
m_start
);
// look for sign
if
(
ds_pos
!=
len
)
if
(
*
curptr
==
'-'
)
{
// copy the data into the local buffer or tempstr, if
// buffer is too small; replace decimal separator, and
// update data to point to the modified bytes
if
((
len
+
1
)
<
buf
.
size
())
{
{
type
=
value_t
::
number_integer
;
std
::
copy
(
m_start
,
m_end
,
buf
.
data
());
max
=
static_cast
<
uint64_t
>
((
std
::
numeric_limits
<
number_integer_t
>::
max
)())
+
1
;
buf
[
len
]
=
0
;
curptr
++
;
buf
[
ds_pos
]
=
decimal_point_char
;
data
=
buf
.
data
();
}
}
else
else
{
{
type
=
value_t
::
number_unsigned
;
tempstr
.
assign
(
m_start
,
m_end
);
max
=
static_cast
<
uint64_t
>
((
std
::
numeric_limits
<
number_unsigned_t
>::
max
)());
tempstr
[
ds_pos
]
=
decimal_point_char
;
data
=
tempstr
.
c_str
();
}
}
}
}
// count the significant figures
char
*
endptr
=
nullptr
;
for
(;
curptr
<
m_cursor
;
curptr
++
)
value
=
0
;
{
// this calls appropriate overload depending on T
// quickly skip tests if a digit
strtof
(
value
,
data
,
&
endptr
);
if
(
*
curptr
<
'0'
or
*
curptr
>
'9'
)
{
// parsing was successful iff strtof parsed exactly the number
if
(
*
curptr
==
'.'
)
// of characters determined by the lexer (len)
const
bool
ok
=
(
endptr
==
(
data
+
len
));
if
(
ok
and
(
value
==
0.0
)
and
(
*
data
==
'-'
))
{
{
// don't count '.' but change to float
// some implementations forget to negate the zero
type
=
value_t
::
number_float
;
value
=
-
0.0
;
continue
;
}
}
// assume exponent (if not then will fail parse): change to
// float, stop counting and record exponent details
return
ok
;
type
=
value_t
::
number_float
;
break
;
}
}
// skip if definitely not an integer
// integral conversion
if
(
type
!=
value_t
::
number_float
)
{
auto
digit
=
static_cast
<
number_unsigned_t
>
(
*
curptr
-
'0'
);
// overflow if value * 10 + digit > max, move terms around
signed
long
long
parse_integral
(
char
**
endptr
,
/*is_signed*/
std
::
true_type
)
const
// to avoid overflow in intermediate values
if
(
value
>
(
max
-
digit
)
/
10
)
{
{
// overflow
return
std
::
strtoll
(
m_start
,
endptr
,
10
);
type
=
value_t
::
number_float
;
}
}
else
unsigned
long
long
parse_integral
(
char
**
endptr
,
/*is_signed*/
std
::
false_type
)
const
{
{
// no overflow
return
std
::
strtoull
(
m_start
,
endptr
,
10
);
value
=
value
*
10
+
digit
;
}
}
}
}
// save the value (if not a float)
template
<
typename
T
>
if
(
type
==
value_t
::
number_unsigned
)
bool
parse
(
T
&
value
,
/*is_integral=*/
std
::
true_type
)
const
{
{
result
.
m_value
.
number_unsigned
=
value
;
char
*
endptr
=
nullptr
;
errno
=
0
;
// these are thread-local
const
auto
x
=
parse_integral
(
&
endptr
,
std
::
is_signed
<
T
>
());
// called right overload?
static_assert
(
std
::
is_signed
<
T
>
()
==
std
::
is_signed
<
decltype
(
x
)
>
(),
""
);
value
=
static_cast
<
T
>
(
x
);
return
(
x
==
static_cast
<
decltype
(
x
)
>
(
value
))
// x fits into destination T
and
(
x
<
0
)
==
(
value
<
0
)
// preserved sign
//and ((x != 0) or is_integral()) // strto[u]ll did nto fail
and
(
errno
==
0
)
// strto[u]ll did not overflow
and
(
m_start
<
m_end
)
// token was not empty
and
(
endptr
==
m_end
);
// parsed entire token exactly
}
}
else
if
(
type
==
value_t
::
number_integer
)
};
/*!
@brief return number value for number tokens
This function translates the last token into the most appropriate
number type (either integer, unsigned integer or floating point),
which is passed back to the caller via the result parameter.
integral numbers that don't fit into the the range of the respective
type are parsed as number_float_t
floating-point values do not satisfy std::isfinite predicate
are converted to value_t::null
throws if the entire string [m_start .. m_cursor) cannot be
interpreted as a number
@param[out] result @ref basic_json object to receive the number.
@param[in] token the type of the number token
*/
bool
get_number
(
basic_json
&
result
,
const
token_type
token
)
const
{
{
// invariant: if we parsed a '-', the absolute value is between
assert
(
m_start
!=
nullptr
);
// 0 (we allow -0) and max == -INT64_MIN
assert
(
m_start
<
m_cursor
);
assert
(
value
>=
0
);
assert
((
token
==
token_type
::
value_unsigned
)
or
assert
(
value
<=
max
);
(
token
==
token_type
::
value_integer
)
or
(
token
==
token_type
::
value_float
));
if
(
value
==
max
)
strtonum
num_converter
(
reinterpret_cast
<
const
char
*>
(
m_start
),
reinterpret_cast
<
const
char
*>
(
m_cursor
));
switch
(
token
)
{
case
lexer
:
:
token_type
::
value_unsigned
:
{
{
// we cannot simply negate value (== max == -INT64_MIN),
number_unsigned_t
val
;
// see https://github.com/nlohmann/json/issues/389
if
(
num_converter
.
to
(
val
))
result
.
m_value
.
number_integer
=
static_cast
<
number_integer_t
>
(
INT64_MIN
);
{
// parsing successful
result
.
m_type
=
value_t
::
number_unsigned
;
result
.
m_value
=
val
;
return
true
;
}
}
else
break
;
}
case
lexer
:
:
token_type
::
value_integer
:
{
{
// all other values can be negated safely
number_integer_t
val
;
result
.
m_value
.
number_integer
=
-
static_cast
<
number_integer_t
>
(
value
);
if
(
num_converter
.
to
(
val
))
{
// parsing successful
result
.
m_type
=
value_t
::
number_integer
;
result
.
m_value
=
val
;
return
true
;
}
}
break
;
}
}
else
default
:
{
break
;
}
}
// parse float (either explicitly or because a previous conversion
// failed)
number_float_t
val
;
if
(
num_converter
.
to
(
val
))
{
{
// parse with strtod
// parsing successful
result
.
m_value
.
number_float
=
str_to_float_t
(
static_cast
<
number_float_t
*>
(
nullptr
),
nullptr
);
result
.
m_type
=
value_t
::
number_float
;
result
.
m_value
=
val
;
// replace infinity and NAN by null
// replace infinity and NAN by null
if
(
not
std
::
isfinite
(
result
.
m_value
.
number_float
))
if
(
not
std
::
isfinite
(
result
.
m_value
.
number_float
))
{
{
type
=
value_t
::
null
;
result
.
m_type
=
value_t
::
null
;
result
.
m_value
=
basic_json
::
json_value
();
result
.
m_value
=
basic_json
::
json_value
();
}
}
return
true
;
}
}
//
save the type
//
couldn't parse number in any format
re
sult
.
m_type
=
typ
e
;
re
turn
fals
e
;
}
}
private
:
private
:
...
@@ -11261,10 +11402,20 @@ basic_json_parser_66:
...
@@ -11261,10 +11402,20 @@ basic_json_parser_66:
break
;
break
;
}
}
case
lexer
:
:
token_type
::
value_number
:
case
lexer
:
:
token_type
::
value_unsigned
:
case
lexer
:
:
token_type
::
value_integer
:
case
lexer
:
:
token_type
::
value_float
:
{
{
m_lexer
.
get_number
(
result
);
const
bool
ok
=
m_lexer
.
get_number
(
result
,
last_token
);
get_token
();
get_token
();
// if number conversion was unsuccessful, then is is
// because the number was directly followed by an
// unexpected character (e.g. "01" where "1" is unexpected)
if
(
not
ok
)
{
unexpect
(
last_token
);
}
break
;
break
;
}
}
...
...
src/json.hpp.re2c
View file @
22b9a301
...
@@ -9444,7 +9444,9 @@ class basic_json
...
@@ -9444,7 +9444,9 @@ class basic_json
literal_false, ///< the `false` literal
literal_false, ///< the `false` literal
literal_null, ///< the `null` literal
literal_null, ///< the `null` literal
value_string, ///< a string -- use get_string() for actual value
value_string, ///< a string -- use get_string() for actual value
value_number, ///< a number -- use get_number() for actual value
value_unsigned, ///< an unsigned integer -- use get_number() for actual value
value_integer, ///< a signed integer -- use get_number() for actual value
value_float, ///< an floating point number -- use get_number() for actual value
begin_array, ///< the character for array begin `[`
begin_array, ///< the character for array begin `[`
begin_object, ///< the character for object begin `{`
begin_object, ///< the character for object begin `{`
end_array, ///< the character for array end `]`
end_array, ///< the character for array end `]`
...
@@ -9596,7 +9598,9 @@ class basic_json
...
@@ -9596,7 +9598,9 @@ class basic_json
return "null literal";
return "null literal";
case token_type::value_string:
case token_type::value_string:
return "string literal";
return "string literal";
case token_type::value_number:
case lexer::token_type::value_unsigned:
case lexer::token_type::value_integer:
case lexer::token_type::value_float:
return "number literal";
return "number literal";
case token_type::begin_array:
case token_type::begin_array:
return "'['";
return "'['";
...
@@ -9694,8 +9698,12 @@ class basic_json
...
@@ -9694,8 +9698,12 @@ class basic_json
exp = e (minus | plus)? digit+;
exp = e (minus | plus)? digit+;
frac = decimal_point digit+;
frac = decimal_point digit+;
int = (zero | digit_1_9 digit*);
int = (zero | digit_1_9 digit*);
number = minus? int frac? exp?;
number_unsigned = int;
number { last_token_type = token_type::value_number; break; }
number_unsigned { last_token_type = token_type::value_unsigned; break; }
number_integer = minus int;
number_integer { last_token_type = token_type::value_integer; break; }
number_float = minus? int frac? exp?;
number_float { last_token_type = token_type::value_float; break; }
// string
// string
quotation_mark = "\"";
quotation_mark = "\"";
...
@@ -9988,186 +9996,241 @@ class basic_json
...
@@ -9988,186 +9996,241 @@ class basic_json
return result;
return result;
}
}
/*!
@brief parse floating point number
This function (and its overloads) serves to select the most appropriate
/*!
standard floating point number parsing function based on the type
@brief parse string into a built-in arithmetic type as if the current
supplied via the first parameter. Set this to @a
locale is POSIX.
static_cast<number_float_t*>(nullptr).
@
param[in,out] endptr receives a pointer to the first character after
@
note in floating-point case strtod may parse past the token's end -
the numbe
r
this is not an erro
r
@
return the floating point number
@
note any leading blanks are not handled
*/
*/
long double str_to_float_t(long double* /* type */, char** endptr) const
struct strtonum
{
{
return std::strtold(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr);
public:
}
strtonum(const char* start, const char* end)
: m_start(start), m_end(end)
{}
/*!
/*!
@brief parse floating point number
@return true iff parsed successfully as number of type T
This function (and its overloads) serves to select the most appropriate
standard floating point number parsing function based on the type
supplied via the first parameter. Set this to @a
static_cast<number_float_t*>(nullptr).
@param[in,out] endptr receives a pointer to the first character after
@param[in,out] val shall contain parsed value, or undefined value
the number
if could not parse
@return the floating point number
*/
*/
double str_to_float_t(double* /* type */, char** endptr) const
template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value>::type>
bool to(T& val) const
{
{
return std::strtod(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr
);
return parse(val, std::is_integral<T>()
);
}
}
/*!
private:
@brief parse floating point number
const char* const m_start = nullptr;
const char* const m_end = nullptr;
This function (and its overloads) serves to select the most appropriate
standard floating point number parsing function based on the type
supplied via the first parameter. Set this to @a
static_cast<number_float_t*>(nullptr).
@param[in,out] endptr receives a pointer to the first character after
// floating-point conversion
the number
@return the floating point number
// overloaded wrappers for strtod/strtof/strtold
*/
// that will be called from parse<floating_point_t>
float str_to_float_t(float* /* type */, char** endptr) const
static void strtof(float& f, const char* str, char** endptr)
{
{
return std::strtof(reinterpret_cast<typename string_t::const_pointer>(m_start)
, endptr);
f = std::strtof(str
, endptr);
}
}
/*!
static void strtof(double& f, const char* str, char** endptr)
@brief return number value for number tokens
{
f = std::strtod(str, endptr);
This function translates the last token into the most appropriate
}
number type (either integer, unsigned integer or floating point),
which is passed back to the caller via the result parameter.
This function parses the integer component up to the radix point or
exponent while collecting information about the 'floating point
representation', which it stores in the result parameter. If there is
no radix point or exponent, and the number can fit into a @ref
number_integer_t or @ref number_unsigned_t then it sets the result
parameter accordingly.
If the number is a floating point number the number is then parsed
static void strtof(long double& f, const char* str, char** endptr)
using @a std:strtod (or @a std:strtof or @a std::strtold).
{
f = std::strtold(str, endptr);
}
@param[out] result @ref basic_json object to receive the number, or
template<typename T>
NAN if the conversion read past the current token. The latter case
bool parse(T& value, /*is_integral=*/std::false_type) const
needs to be treated by the caller function.
*/
void get_number(basic_json& result) const
{
{
assert(m_start != nullptr);
// replace decimal separator with locale-specific version,
// when necessary; data will point to either the original
// string, or buf, or tempstr containing the fixed string.
std::string tempstr;
std::array<char, 64> buf;
const size_t len = static_cast<size_t>(m_end - m_start);
const lexer::lexer_char_t* curptr = m_start;
// lexer will reject empty numbers
assert(len > 0);
// accumulate the integer conversion result (unsigned for now)
// since dealing with strtod family of functions, we're
number_unsigned_t value = 0;
// getting the decimal point char from the C locale facilities
// instead of C++'s numpunct facet of the current std::locale
const auto loc = localeconv();
assert(loc != nullptr);
const char decimal_point_char = (loc->decimal_point == nullptr) ? '.' : loc->decimal_point[0];
// maximum absolute value of the relevant integer type
const char* data = m_start;
number_unsigned_t max;
// temporarily store the type to avoid unnecessary bitfield access
if (decimal_point_char != '.')
value_t type;
{
const size_t ds_pos = static_cast<size_t>(std::find(m_start, m_end, '.') - m_start);
// look for sign
if (ds_pos != len)
if (*curptr == '-')
{
// copy the data into the local buffer or tempstr, if
// buffer is too small; replace decimal separator, and
// update data to point to the modified bytes
if ((len + 1) < buf.size())
{
{
type = value_t::number_integer;
std::copy(m_start, m_end, buf.data());
max = static_cast<uint64_t>((std::numeric_limits<number_integer_t>::max)()) + 1;
buf[len] = 0;
curptr++;
buf[ds_pos] = decimal_point_char;
data = buf.data();
}
}
else
else
{
{
type = value_t::number_unsigned;
tempstr.assign(m_start, m_end);
max = static_cast<uint64_t>((std::numeric_limits<number_unsigned_t>::max)());
tempstr[ds_pos] = decimal_point_char;
data = tempstr.c_str();
}
}
}
}
// count the significant figures
char* endptr = nullptr;
for (; curptr < m_cursor; curptr++)
value = 0;
{
// this calls appropriate overload depending on T
// quickly skip tests if a digit
strtof(value, data, &endptr);
if (*curptr < '0' or* curptr > '9')
{
// parsing was successful iff strtof parsed exactly the number
if (*curptr == '.')
// of characters determined by the lexer (len)
const bool ok = (endptr == (data + len));
if (ok and (value == 0.0) and (*data == '-'))
{
{
// don't count '.' but change to float
// some implementations forget to negate the zero
type = value_t::number_float;
value = -0.0;
continue;
}
}
// assume exponent (if not then will fail parse): change to
// float, stop counting and record exponent details
return ok;
type = value_t::number_float;
break;
}
}
// skip if definitely not an integer
// integral conversion
if (type != value_t::number_float)
{
auto digit = static_cast<number_unsigned_t>(*curptr - '0');
// overflow if value * 10 + digit > max, move terms around
signed long long parse_integral(char** endptr, /*is_signed*/std::true_type) const
// to avoid overflow in intermediate values
if (value > (max - digit) / 10)
{
{
// overflow
return std::strtoll(m_start, endptr, 10);
type = value_t::number_float;
}
}
else
unsigned long long parse_integral(char** endptr, /*is_signed*/std::false_type) const
{
{
// no overflow
return std::strtoull(m_start, endptr, 10);
value = value * 10 + digit;
}
}
}
}
// save the value (if not a float)
template<typename T>
if (type == value_t::number_unsigned)
bool parse(T& value, /*is_integral=*/std::true_type) const
{
{
result.m_value.number_unsigned = value;
char* endptr = nullptr;
errno = 0; // these are thread-local
const auto x = parse_integral(&endptr, std::is_signed<T>());
// called right overload?
static_assert(std::is_signed<T>() == std::is_signed<decltype(x)>(), "");
value = static_cast<T>(x);
return (x == static_cast<decltype(x)>(value)) // x fits into destination T
and (x < 0) == (value < 0) // preserved sign
//and ((x != 0) or is_integral()) // strto[u]ll did nto fail
and (errno == 0) // strto[u]ll did not overflow
and (m_start < m_end) // token was not empty
and (endptr == m_end); // parsed entire token exactly
}
}
else if (type == value_t::number_integer)
};
/*!
@brief return number value for number tokens
This function translates the last token into the most appropriate
number type (either integer, unsigned integer or floating point),
which is passed back to the caller via the result parameter.
integral numbers that don't fit into the the range of the respective
type are parsed as number_float_t
floating-point values do not satisfy std::isfinite predicate
are converted to value_t::null
throws if the entire string [m_start .. m_cursor) cannot be
interpreted as a number
@param[out] result @ref basic_json object to receive the number.
@param[in] token the type of the number token
*/
bool get_number(basic_json& result, const token_type token) const
{
{
// invariant: if we parsed a '-', the absolute value is between
assert(m_start != nullptr);
// 0 (we allow -0) and max == -INT64_MIN
assert(m_start < m_cursor);
assert(value >= 0);
assert((token == token_type::value_unsigned) or
assert(value <= max);
(token == token_type::value_integer) or
(token == token_type::value_float));
strtonum num_converter(reinterpret_cast<const char*>(m_start),
reinterpret_cast<const char*>(m_cursor));
if (value == max
)
switch (token
)
{
{
// we cannot simply negate value (== max == -INT64_MIN),
case lexer::token_type::value_unsigned:
// see https://github.com/nlohmann/json/issues/389
{
result.m_value.number_integer = static_cast<number_integer_t>(INT64_MIN);
number_unsigned_t val;
if (num_converter.to(val))
{
// parsing successful
result.m_type = value_t::number_unsigned;
result.m_value = val;
return true;
}
}
else
break;
}
case lexer::token_type::value_integer:
{
number_integer_t val;
if (num_converter.to(val))
{
{
// all other values can be negated safely
// parsing successful
result.m_value.number_integer = -static_cast<number_integer_t>(value);
result.m_type = value_t::number_integer;
result.m_value = val;
return true;
}
}
break;
}
}
else
default:
{
{
// parse with strtod
break;
result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), nullptr);
}
}
// parse float (either explicitly or because a previous conversion
// failed)
number_float_t val;
if (num_converter.to(val))
{
// parsing successful
result.m_type = value_t::number_float;
result.m_value = val;
// replace infinity and NAN by null
// replace infinity and NAN by null
if (not std::isfinite(result.m_value.number_float))
if (not std::isfinite(result.m_value.number_float))
{
{
type
= value_t::null;
result.m_type
= value_t::null;
result.m_value = basic_json::json_value();
result.m_value = basic_json::json_value();
}
}
return true;
}
}
//
save the type
//
couldn't parse number in any format
re
sult.m_type = typ
e;
re
turn fals
e;
}
}
private:
private:
...
@@ -10411,10 +10474,20 @@ class basic_json
...
@@ -10411,10 +10474,20 @@ class basic_json
break;
break;
}
}
case lexer::token_type::value_number:
case lexer::token_type::value_unsigned:
case lexer::token_type::value_integer:
case lexer::token_type::value_float:
{
{
m_lexer.get_number(result
);
const bool ok = m_lexer.get_number(result, last_token
);
get_token();
get_token();
// if number conversion was unsuccessful, then is is
// because the number was directly followed by an
// unexpected character (e.g. "01" where "1" is unexpected)
if (not ok)
{
unexpect(last_token);
}
break;
break;
}
}
...
...
test/src/unit-class_lexer.cpp
View file @
22b9a301
...
@@ -65,25 +65,37 @@ TEST_CASE("lexer class")
...
@@ -65,25 +65,37 @@ TEST_CASE("lexer class")
SECTION
(
"numbers"
)
SECTION
(
"numbers"
)
{
{
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"0"
),
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"0"
),
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
number
));
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
unsigned
));
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"1"
),
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"1"
),
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
number
));
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
unsigned
));
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"2"
),
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"2"
),
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
number
));
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
unsigned
));
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"3"
),
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"3"
),
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
number
));
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
unsigned
));
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"4"
),
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"4"
),
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
number
));
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
unsigned
));
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"5"
),
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"5"
),
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
number
));
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
unsigned
));
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"6"
),
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"6"
),
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
number
));
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
unsigned
));
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"7"
),
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"7"
),
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
number
));
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
unsigned
));
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"8"
),
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"8"
),
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
number
));
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_
unsigned
));
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"9"
),
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"9"
),
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_number
));
1
).
scan
()
==
json
::
lexer
::
token_type
::
value_unsigned
));
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"-0"
),
2
).
scan
()
==
json
::
lexer
::
token_type
::
value_integer
));
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"-1"
),
2
).
scan
()
==
json
::
lexer
::
token_type
::
value_integer
));
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"1.1"
),
3
).
scan
()
==
json
::
lexer
::
token_type
::
value_float
));
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"-1.1"
),
4
).
scan
()
==
json
::
lexer
::
token_type
::
value_float
));
CHECK
((
json
::
lexer
(
reinterpret_cast
<
const
json
::
lexer
::
lexer_char_t
*>
(
"1E10"
),
4
).
scan
()
==
json
::
lexer
::
token_type
::
value_float
));
}
}
SECTION
(
"whitespace"
)
SECTION
(
"whitespace"
)
...
@@ -109,7 +121,9 @@ TEST_CASE("lexer class")
...
@@ -109,7 +121,9 @@ TEST_CASE("lexer class")
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
literal_false
)
==
"false literal"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
literal_false
)
==
"false literal"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
literal_null
)
==
"null literal"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
literal_null
)
==
"null literal"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
value_string
)
==
"string literal"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
value_string
)
==
"string literal"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
value_number
)
==
"number literal"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
value_unsigned
)
==
"number literal"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
value_integer
)
==
"number literal"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
value_float
)
==
"number literal"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
begin_array
)
==
"'['"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
begin_array
)
==
"'['"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
begin_object
)
==
"'{'"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
begin_object
)
==
"'{'"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
end_array
)
==
"']'"
));
CHECK
((
json
::
lexer
::
token_type_name
(
json
::
lexer
::
token_type
::
end_array
)
==
"']'"
));
...
...
test/src/unit-class_parser.cpp
View file @
22b9a301
...
@@ -101,6 +101,7 @@ TEST_CASE("parser class")
...
@@ -101,6 +101,7 @@ TEST_CASE("parser class")
CHECK_THROWS_WITH
(
json
::
parser
(
"
\"\b\"
"
).
parse
(),
"parse error - unexpected '
\"
'"
);
CHECK_THROWS_WITH
(
json
::
parser
(
"
\"\b\"
"
).
parse
(),
"parse error - unexpected '
\"
'"
);
// improve code coverage
// improve code coverage
CHECK_THROWS_AS
(
json
::
parser
(
"\uFF01"
).
parse
(),
std
::
invalid_argument
);
CHECK_THROWS_AS
(
json
::
parser
(
"\uFF01"
).
parse
(),
std
::
invalid_argument
);
CHECK_THROWS_AS
(
json
::
parser
(
"[-4:1,]"
).
parse
(),
std
::
invalid_argument
);
// unescaped control characters
// unescaped control characters
CHECK_THROWS_AS
(
json
::
parser
(
"
\"\x00\"
"
).
parse
(),
std
::
invalid_argument
);
CHECK_THROWS_AS
(
json
::
parser
(
"
\"\x00\"
"
).
parse
(),
std
::
invalid_argument
);
CHECK_THROWS_AS
(
json
::
parser
(
"
\"\x01\"
"
).
parse
(),
std
::
invalid_argument
);
CHECK_THROWS_AS
(
json
::
parser
(
"
\"\x01\"
"
).
parse
(),
std
::
invalid_argument
);
...
@@ -269,6 +270,11 @@ TEST_CASE("parser class")
...
@@ -269,6 +270,11 @@ TEST_CASE("parser class")
}
}
}
}
SECTION
(
"overflow"
)
{
CHECK
(
json
::
parser
(
"1.18973e+4932"
).
parse
()
==
json
());
}
SECTION
(
"invalid numbers"
)
SECTION
(
"invalid numbers"
)
{
{
CHECK_THROWS_AS
(
json
::
parser
(
"01"
).
parse
(),
std
::
invalid_argument
);
CHECK_THROWS_AS
(
json
::
parser
(
"01"
).
parse
(),
std
::
invalid_argument
);
...
@@ -293,7 +299,7 @@ TEST_CASE("parser class")
...
@@ -293,7 +299,7 @@ TEST_CASE("parser class")
CHECK_THROWS_AS
(
json
::
parser
(
"+0"
).
parse
(),
std
::
invalid_argument
);
CHECK_THROWS_AS
(
json
::
parser
(
"+0"
).
parse
(),
std
::
invalid_argument
);
CHECK_THROWS_WITH
(
json
::
parser
(
"01"
).
parse
(),
CHECK_THROWS_WITH
(
json
::
parser
(
"01"
).
parse
(),
"parse error - unexpected number literal
; expected end of input
"
);
"parse error - unexpected number literal"
);
CHECK_THROWS_WITH
(
json
::
parser
(
"--1"
).
parse
(),
"parse error - unexpected '-'"
);
CHECK_THROWS_WITH
(
json
::
parser
(
"--1"
).
parse
(),
"parse error - unexpected '-'"
);
CHECK_THROWS_WITH
(
json
::
parser
(
"1."
).
parse
(),
CHECK_THROWS_WITH
(
json
::
parser
(
"1."
).
parse
(),
"parse error - unexpected '.'; expected end of input"
);
"parse error - unexpected '.'; expected end of input"
);
...
...
test/src/unit-regression.cpp
View file @
22b9a301
...
@@ -383,7 +383,7 @@ TEST_CASE("regression tests")
...
@@ -383,7 +383,7 @@ TEST_CASE("regression tests")
};
};
// change locale to mess with decimal points
// change locale to mess with decimal points
std
::
locale
::
global
(
std
::
locale
(
std
::
locale
(),
new
CommaDecimalSeparator
));
auto
orig_locale
=
std
::
locale
::
global
(
std
::
locale
(
std
::
locale
(),
new
CommaDecimalSeparator
));
CHECK
(
j1a
.
dump
()
==
"23.42"
);
CHECK
(
j1a
.
dump
()
==
"23.42"
);
CHECK
(
j1b
.
dump
()
==
"23.42"
);
CHECK
(
j1b
.
dump
()
==
"23.42"
);
...
@@ -407,8 +407,34 @@ TEST_CASE("regression tests")
...
@@ -407,8 +407,34 @@ TEST_CASE("regression tests")
CHECK
(
j3c
.
dump
()
==
"10000"
);
CHECK
(
j3c
.
dump
()
==
"10000"
);
//CHECK(j3b.dump() == "1E04"); // roundtrip error
//CHECK(j3b.dump() == "1E04"); // roundtrip error
//CHECK(j3c.dump() == "1e04"); // roundtrip error
//CHECK(j3c.dump() == "1e04"); // roundtrip error
std
::
locale
::
global
(
orig_locale
);
}
}
SECTION
(
"issue #379 - locale-independent str-to-num"
)
{
setlocale
(
LC_NUMERIC
,
"de_DE.UTF-8"
);
// disabled, because locale-specific beharivor is not
// triggered in AppVeyor for some reason
#ifndef _MSC_VER
{
// verify that strtod now uses commas as decimal-separator
CHECK
(
std
::
strtod
(
"3,14"
,
nullptr
)
==
3.14
);
// verify that strtod does not understand dots as decimal separator
CHECK
(
std
::
strtod
(
"3.14"
,
nullptr
)
==
3
);
}
#endif
// verify that parsed correctly despite using strtod internally
CHECK
(
json
::
parse
(
"3.14"
).
get
<
double
>
()
==
3.14
);
// check a different code path
CHECK
(
json
::
parse
(
"1.000000000000000000000000000000000000000000000000000000000000000000000000"
).
get
<
double
>
()
==
1.0
);
}
SECTION
(
"issue #233 - Can't use basic_json::iterator as a base iterator for std::move_iterator"
)
SECTION
(
"issue #233 - Can't use basic_json::iterator as a base iterator for std::move_iterator"
)
{
{
json
source
=
{
"a"
,
"b"
,
"c"
};
json
source
=
{
"a"
,
"b"
,
"c"
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment