Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
J
json
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
json
Commits
c87ffad4
Unverified
Commit
c87ffad4
authored
Mar 17, 2018
by
Niels Lohmann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
♻
implemented a non-recursive parser
parent
27cf05af
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
487 additions
and
259 deletions
+487
-259
include/nlohmann/detail/input/parser.hpp
include/nlohmann/detail/input/parser.hpp
+247
-131
single_include/nlohmann/json.hpp
single_include/nlohmann/json.hpp
+240
-128
No files found.
include/nlohmann/detail/input/parser.hpp
View file @
c87ffad4
...
...
@@ -431,187 +431,303 @@ class parser
bool
sax_parse_internal
(
json_sax_t
*
sax
)
{
switch
(
last_token
)
// two values for the structured values
enum
class
parse_state_t
{
array_value
,
object_value
};
// stack to remember the hieararchy of structured values we are parsing
std
::
vector
<
parse_state_t
>
states
;
// value to avoid a goto (see comment where set to true)
bool
skip_to_state_evaluation
=
false
;
while
(
true
)
{
case
token_type
:
:
begin_object
:
if
(
not
skip_to_state_evaluation
)
{
if
(
not
sax
->
start_object
())
// invariant: get_token() was called before each iteration
switch
(
last_token
)
{
return
false
;
}
case
token_type
:
:
begin_object
:
{
if
(
not
sax
->
start_object
())
{
return
false
;
}
// read next token
get_token
();
// read next token
get_token
();
// closing } -> we are done
if
(
JSON_UNLIKELY
(
last_token
==
token_type
::
end_object
))
{
return
sax
->
end_object
();
}
// closing } -> we are done
if
(
last_token
==
token_type
::
end_object
)
{
if
(
not
sax
->
end_object
())
{
return
false
;
}
break
;
}
// parse values
while
(
true
)
{
// parse key
if
(
JSON_UNLIKELY
(
last_token
!=
token_type
::
value_string
))
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
value_string
)));
// parse key
if
(
JSON_UNLIKELY
(
last_token
!=
token_type
::
value_string
))
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
value_string
)));
}
else
{
if
(
not
sax
->
key
(
m_lexer
.
move_string
()))
{
return
false
;
}
}
// parse separator (:)
get_token
();
if
(
JSON_UNLIKELY
(
last_token
!=
token_type
::
name_separator
))
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
name_separator
)));
}
// remember we are now inside an object
states
.
push_back
(
parse_state_t
::
object_value
);
// parse values
get_token
();
continue
;
}
else
case
token_type
:
:
begin_array
:
{
if
(
not
sax
->
key
(
m_lexer
.
move_string
()
))
if
(
not
sax
->
start_array
(
))
{
return
false
;
}
// read next token
get_token
();
// closing ] -> we are done
if
(
last_token
==
token_type
::
end_array
)
{
if
(
not
sax
->
end_array
())
{
return
false
;
}
break
;
}
// remember we are now inside an array
states
.
push_back
(
parse_state_t
::
array_value
);
// parse values (no need to call get_token)
continue
;
}
// parse separator (:)
get_token
();
if
(
JSON_UNLIKELY
(
last_token
!=
token_type
::
name_separator
))
case
token_type
:
:
value_float
:
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
name_separator
)));
const
auto
res
=
m_lexer
.
get_number_float
();
if
(
JSON_UNLIKELY
(
not
std
::
isfinite
(
res
)))
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
out_of_range
::
create
(
406
,
"number overflow parsing '"
+
m_lexer
.
get_token_string
()
+
"'"
));
}
else
{
if
(
not
sax
->
number_float
(
res
,
m_lexer
.
move_string
()))
{
return
false
;
}
break
;
}
}
// parse value
get_token
();
if
(
not
sax_parse_internal
(
sax
))
case
token_type
:
:
literal_false
:
{
return
false
;
if
(
not
sax
->
boolean
(
false
))
{
return
false
;
}
break
;
}
// comma -> next value
get_token
();
if
(
last_token
==
token_type
::
value_separator
)
case
token_type
:
:
literal_null
:
{
get_token
();
continue
;
if
(
not
sax
->
null
())
{
return
false
;
}
break
;
}
// closing }
if
(
JSON_LIKELY
(
last_token
==
token_type
::
end_object
))
case
token_type
:
:
literal_true
:
{
return
sax
->
end_object
();
if
(
not
sax
->
boolean
(
true
))
{
return
false
;
}
break
;
}
else
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
end_object
)));
case
token_type
:
:
value_integer
:
{
if
(
not
sax
->
number_integer
(
m_lexer
.
get_number_integer
()))
{
return
false
;
}
break
;
}
}
}
case
token_type
:
:
begin_array
:
{
if
(
not
sax
->
start_array
())
{
return
false
;
}
// read next token
get_token
();
// closing ] -> we are done
if
(
last_token
==
token_type
::
end_array
)
{
return
sax
->
end_array
();
}
// parse values
while
(
true
)
{
// parse value
if
(
not
sax_parse_internal
(
sax
))
case
token_type
:
:
value_string
:
{
return
false
;
if
(
not
sax
->
string
(
m_lexer
.
move_string
()))
{
return
false
;
}
break
;
}
// comma -> next value
get_token
();
if
(
last_token
==
token_type
::
value_separator
)
case
token_type
:
:
value_unsigned
:
{
get_token
();
continue
;
if
(
not
sax
->
number_unsigned
(
m_lexer
.
get_number_unsigned
()))
{
return
false
;
}
break
;
}
// closing ]
if
(
JSON_LIKELY
(
last_token
==
token_type
::
end_array
))
case
token_type
:
:
parse_error
:
{
return
sax
->
end_array
();
// using "uninitialized" to avoid "expected" message
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
uninitialized
)));
}
else
default:
// the last token was unexpected
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
end_array
)));
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
literal_or_value
)));
}
}
}
case
token_type
:
:
value_float
:
{
const
auto
res
=
m_lexer
.
get_number_float
();
if
(
JSON_UNLIKELY
(
not
std
::
isfinite
(
res
)))
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
out_of_range
::
create
(
406
,
"number overflow parsing '"
+
m_lexer
.
get_token_string
()
+
"'"
));
}
else
{
return
sax
->
number_float
(
res
,
m_lexer
.
move_string
());
}
}
case
token_type
:
:
literal_false
:
{
return
sax
->
boolean
(
false
);
}
case
token_type
:
:
literal_null
:
{
return
sax
->
null
();
}
case
token_type
:
:
literal_true
:
else
{
return
sax
->
boolean
(
true
)
;
skip_to_state_evaluation
=
false
;
}
case
token_type
:
:
value_integer
:
// we reached this line after we successfully parsed a value
if
(
states
.
empty
())
{
return
sax
->
number_integer
(
m_lexer
.
get_number_integer
());
// empty stack: we reached the end of the hieararchy: done
return
true
;
}
case
token_type
:
:
value_string
:
else
{
return
sax
->
string
(
m_lexer
.
move_string
());
}
get_token
();
switch
(
states
.
back
())
{
case
parse_state_t
:
:
array_value
:
{
// comma -> next value
if
(
last_token
==
token_type
::
value_separator
)
{
// parse a new value
get_token
();
continue
;
}
case
token_type
:
:
value_unsigned
:
{
return
sax
->
number_unsigned
(
m_lexer
.
get_number_unsigned
());
}
// closing ]
if
(
JSON_LIKELY
(
last_token
==
token_type
::
end_array
))
{
if
(
not
sax
->
end_array
())
{
return
false
;
}
// We are done with this array. Before we can parse
// a new value, we need to evaluate the new state
// first. By setting skip_to_state_evaluation to
// false, we are effectively jumping to the
// beginning of this switch.
assert
(
not
states
.
empty
());
states
.
pop_back
();
skip_to_state_evaluation
=
true
;
continue
;
}
else
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
end_array
)));
}
}
case
token_type
:
:
parse_error
:
{
// using "uninitialized" to avoid "expected" message
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
uninitialized
)));
}
case
parse_state_t
:
:
object_value
:
{
// comma -> next value
if
(
last_token
==
token_type
::
value_separator
)
{
get_token
();
// parse key
if
(
JSON_UNLIKELY
(
last_token
!=
token_type
::
value_string
))
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
value_string
)));
}
else
{
if
(
not
sax
->
key
(
m_lexer
.
move_string
()))
{
return
false
;
}
}
// parse separator (:)
get_token
();
if
(
JSON_UNLIKELY
(
last_token
!=
token_type
::
name_separator
))
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
name_separator
)));
}
// parse values
get_token
();
continue
;
}
default:
// the last token was unexpected
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
literal_or_value
)));
// closing }
if
(
JSON_LIKELY
(
last_token
==
token_type
::
end_object
))
{
if
(
not
sax
->
end_object
())
{
return
false
;
}
// We are done with this object. Before we can
// parse a new value, we need to evaluate the new
// state first. By setting skip_to_state_evaluation
// to false, we are effectively jumping to the
// beginning of this switch.
assert
(
not
states
.
empty
());
states
.
pop_back
();
skip_to_state_evaluation
=
true
;
continue
;
}
else
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
end_object
)));
}
}
}
}
}
}
...
...
single_include/nlohmann/json.hpp
View file @
c87ffad4
...
...
@@ -3941,187 +3941,299 @@ class parser
bool sax_parse_internal(json_sax_t* sax)
{
switch
(
last_token
)
// two values for the structured values
enum class parse_state_t { array_value, object_value };
// stack to remember the hieararchy of structured values we are parsing
std::vector<parse_state_t> states;
// value to avoid a goto (see comment where set to true)
bool skip_to_tail = false;
while (true)
{
case
token_type
:
:
begin_object
:
if (not skip_to_tail)
{
if
(
not
sax
->
start_object
())
// invariant: get_token() was called before each iteration
switch (last_token)
{
return
false
;
}
case token_type::begin_object:
{
if (not sax->start_object())
{
return false;
}
// read next token
get_token
();
// read next token
get_token();
// closing } -> we are done
if
(
JSON_UNLIKELY
(
last_token
==
token_type
::
end_object
))
{
return
sax
->
end_object
();
}
// closing } -> we are done
if (last_token == token_type::end_object)
{
if (not sax->end_object())
{
return false;
}
break;
}
// parse values
while
(
true
)
{
// parse key
if
(
JSON_UNLIKELY
(
last_token
!=
token_type
::
value_string
))
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
value_string
)));
// parse key
if (JSON_UNLIKELY(last_token != token_type::value_string))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string)));
}
else
{
if (not sax->key(m_lexer.move_string()))
{
return false;
}
}
// parse separator (:)
get_token();
if (JSON_UNLIKELY(last_token != token_type::name_separator))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator)));
}
// parse values
get_token();
states.push_back(parse_state_t::object_value);
continue;
}
else
case token_type::begin_array:
{
if
(
not
sax
->
key
(
m_lexer
.
move_string
()
))
if (not sax->
start_array(
))
{
return false;
}
// read next token
get_token();
// closing ] -> we are done
if (last_token == token_type::end_array)
{
if (not sax->end_array())
{
return false;
}
break;
}
// parse values (no need to call get_token)
states.push_back(parse_state_t::array_value);
continue;
}
// parse separator (:)
get_token
();
if
(
JSON_UNLIKELY
(
last_token
!=
token_type
::
name_separator
))
case token_type::value_float:
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
name_separator
)));
const auto res = m_lexer.get_number_float();
if (JSON_UNLIKELY(not std::isfinite(res)))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'"));
}
else
{
if (not sax->number_float(res, m_lexer.move_string()))
{
return false;
}
break;
}
}
// parse value
get_token
();
if
(
not
sax_parse_internal
(
sax
))
case token_type::literal_false:
{
return
false
;
if (not sax->boolean(false))
{
return false;
}
break;
}
// comma -> next value
get_token
();
if
(
last_token
==
token_type
::
value_separator
)
case token_type::literal_null:
{
get_token
();
continue
;
if (not sax->null())
{
return false;
}
break;
}
// closing }
if
(
JSON_LIKELY
(
last_token
==
token_type
::
end_object
))
case token_type::literal_true:
{
return
sax
->
end_object
();
if (not sax->boolean(true))
{
return false;
}
break;
}
else
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
end_object
)));
case token_type::value_integer:
{
if (not sax->number_integer(m_lexer.get_number_integer()))
{
return false;
}
break;
}
}
}
case
token_type
:
:
begin_array
:
{
if
(
not
sax
->
start_array
())
{
return
false
;
}
// read next token
get_token
();
// closing ] -> we are done
if
(
last_token
==
token_type
::
end_array
)
{
return
sax
->
end_array
();
}
// parse values
while
(
true
)
{
// parse value
if
(
not
sax_parse_internal
(
sax
))
case token_type::value_string:
{
return
false
;
if (not sax->string(m_lexer.move_string()))
{
return false;
}
break;
}
// comma -> next value
get_token
();
if
(
last_token
==
token_type
::
value_separator
)
case token_type::value_unsigned:
{
get_token
();
continue
;
if (not sax->number_unsigned(m_lexer.get_number_unsigned()))
{
return false;
}
break;
}
// closing ]
if
(
JSON_LIKELY
(
last_token
==
token_type
::
end_array
))
case token_type::parse_error:
{
return
sax
->
end_array
();
// using "uninitialized" to avoid "expected" message
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized)));
}
else
default: // the last token was unexpected
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
end_array
)));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::
literal_or_value
)));
}
}
}
case
token_type
:
:
value_float
:
else
{
const
auto
res
=
m_lexer
.
get_number_float
();
if
(
JSON_UNLIKELY
(
not
std
::
isfinite
(
res
)))
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
out_of_range
::
create
(
406
,
"number overflow parsing '"
+
m_lexer
.
get_token_string
()
+
"'"
));
}
else
{
return
sax
->
number_float
(
res
,
m_lexer
.
move_string
());
}
skip_to_tail = false;
}
case
token_type
:
:
literal_false
:
// we reached this line after we successfully parsed a value
if (states.empty())
{
return
sax
->
boolean
(
false
);
// empty stack: we reached the end of the hieararchy: done
return true;
}
case
token_type
:
:
literal_null
:
else
{
return
sax
->
null
();
}
get_token();
switch (states.back())
{
case parse_state_t::array_value:
{
// comma -> next value
if (last_token == token_type::value_separator)
{
// parse a new value
get_token();
continue;
}
case
token_type
:
:
literal_true
:
{
return
sax
->
boolean
(
true
);
}
// closing ]
if (JSON_LIKELY(last_token == token_type::end_array))
{
if (not sax->end_array())
{
return false;
}
case
token_type
:
:
value_integer
:
{
return
sax
->
number_integer
(
m_lexer
.
get_number_integer
());
}
// We are done with this array. Before we can parse
// a new value, we need to evaluate the new state
// first. By setting skip_to_tail to false, we are
// effectively jumping to the beginning of this
// switch.
assert(not states.empty());
states.pop_back();
skip_to_tail = true;
continue;
}
else
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array)));
}
}
case
token_type
:
:
value_string
:
{
return
sax
->
string
(
m_lexer
.
move_string
());
}
case parse_state_t::object_value:
{
// comma -> next value
if (last_token == token_type::value_separator)
{
get_token();
case
token_type
:
:
value_unsigned
:
{
return
sax
->
number_unsigned
(
m_lexer
.
get_number_unsigned
());
}
// parse key
if (JSON_UNLIKELY(last_token != token_type::value_string))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string)));
}
else
{
if (not sax->key(m_lexer.move_string()))
{
return false;
}
}
case
token_type
:
:
parse_error
:
{
// using "uninitialized" to avoid "expected" message
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
uninitialized
)));
}
// parse separator (:)
get_token();
if (JSON_UNLIKELY(last_token != token_type::name_separator))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator)));
}
default:
// the last token was unexpected
{
return
sax
->
parse_error
(
m_lexer
.
get_position
(),
m_lexer
.
get_token_string
(),
parse_error
::
create
(
101
,
m_lexer
.
get_position
(),
exception_message
(
token_type
::
literal_or_value
)));
// parse values
get_token();
continue;
}
// closing }
if (JSON_LIKELY(last_token == token_type::end_object))
{
if (not sax->end_object())
{
return false;
}
// We are done with this object. Before we can
// parse a new value, we need to evaluate the new
// state first. By setting skip_to_tail to false,
// we are effectively jumping to the beginning of
// this switch.
assert(not states.empty());
states.pop_back();
skip_to_tail = true;
continue;
}
else
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object)));
}
}
}
}
}
}
...
...
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