Skip to content
This repository has been archived by the owner on Mar 26, 2020. It is now read-only.

Commit

Permalink
Add explicit state management.
Browse files Browse the repository at this point in the history
For partial parsing support, we need to remember what we were doing so that we
can continue parsing. This commit adds an enum with all our parsing states and a
states stack to store the current states.
  • Loading branch information
canatella committed Dec 29, 2016
1 parent 62220ad commit 621d48c
Showing 1 changed file with 122 additions and 11 deletions.
133 changes: 122 additions & 11 deletions json11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,30 @@ struct JsonParserPriv final {
const JsonParse strategy;
std::stack<Json> values;

enum State {
EXPECT_VALUE,
VALUE_STRING,
VALUE_NUMBER,
VALUE_TRUE,
VALUE_FALSE,
VALUE_NULL,
VALUE_COMMENT,
VALUE_OBJECT,
OBJECT_KEY_OR_END,
OBJECT_COMMA_OR_END,
OBJECT_KEY,
OBJECT_COLON,
OBJECT_VALUE,
VALUE_ARRAY,
ARRAY_VALUE_OR_END,
ARRAY_COMMA_OR_END,
ARRAY_VALUE
};
std::stack<State> states;

JsonParserPriv(string str, string &err, const JsonParse strategy):
str(str), err(err), strategy(strategy) {
push_state(EXPECT_VALUE);
}

/* fail(msg, err_ret = Json())
Expand All @@ -359,6 +381,7 @@ struct JsonParserPriv final {
template <typename T>
void fail(string &&msg, const T err_ret) {
if (!failed) {
pop_state();
err = std::move(msg);
}

Expand All @@ -367,6 +390,7 @@ struct JsonParserPriv final {
values.push(err_ret);

failed = true;
assert(states.size() > 0);
}

/* stop(msg, err_ret = Json())
Expand All @@ -391,6 +415,33 @@ struct JsonParserPriv final {
return i == str.size();
}

/* set_state()
*
* Set current parsing state.
*/
void set_state(State state) {
states.pop();
states.push(state);
}

/* push_state()
*
* push new current parsing state.
*/
void push_state(State state) {
states.push(state);
}

/* pop_state()
*
* Set current parsing state.
*/
void pop_state() {
states.pop();
}

#define assert_state(S) assert(states.top() == S)

/* consume_whitespace()
*
* Advance until the current character is non-whitespace.
Expand All @@ -405,6 +456,7 @@ struct JsonParserPriv final {
* Advance comments (c-style inline and multiline).
*/
void consume_comment() {
assert_state(VALUE_COMMENT);
bool comment_found = false;
if (str[i] == '/') {
i++;
Expand Down Expand Up @@ -446,6 +498,7 @@ struct JsonParserPriv final {
else
return fail("malformed comment", false);
}
pop_state();
values.push(comment_found);
}

Expand All @@ -458,6 +511,7 @@ struct JsonParserPriv final {
if(strategy == JsonParse::COMMENTS) {
bool comment_found = false;
do {
push_state(VALUE_COMMENT);
consume_comment();
if (need_data)
break;
Expand Down Expand Up @@ -515,6 +569,8 @@ struct JsonParserPriv final {
* Parse a string, starting at the current position.
*/
void parse_string() {
assert_state(VALUE_STRING);

string out;
long last_escaped_codepoint = -1;
while (true) {
Expand All @@ -525,6 +581,7 @@ struct JsonParserPriv final {

if (ch == '"') {
encode_utf8(last_escaped_codepoint, out);
pop_state();
values.push(out);
return;
}
Expand Down Expand Up @@ -609,6 +666,7 @@ struct JsonParserPriv final {
* Parse a double.
*/
void parse_number() {
assert_state(VALUE_NUMBER);
size_t start_pos = i;

if (str[i] == '-')
Expand Down Expand Up @@ -641,6 +699,7 @@ struct JsonParserPriv final {

if (str[i] != '.' && str[i] != 'e' && str[i] != 'E'
&& (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) {
pop_state();
return values.push(std::atoi(str.c_str() + start_pos));
}

Expand Down Expand Up @@ -690,6 +749,7 @@ struct JsonParserPriv final {
}
}

pop_state();
return values.push(std::strtod(str.c_str() + start_pos, nullptr));
}

Expand All @@ -707,6 +767,7 @@ struct JsonParserPriv final {

if (str.compare(i, expected.length(), expected) == 0) {
i += expected.length();
pop_state();
return values.push(res);
} else {
return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length()));
Expand All @@ -718,6 +779,7 @@ struct JsonParserPriv final {
* Parse a json true value.
*/
void parse_true() {
assert_state(VALUE_TRUE);
expect("true", true);
}

Expand All @@ -726,6 +788,7 @@ struct JsonParserPriv final {
* Parse a json false value.
*/
void parse_false() {
assert_state(VALUE_FALSE);
expect("false", false);
}

Expand All @@ -734,6 +797,7 @@ struct JsonParserPriv final {
* Parse a json null value.
*/
void parse_null() {
assert_state(VALUE_NULL);
expect("null", Json());
}

Expand All @@ -742,10 +806,16 @@ struct JsonParserPriv final {
* Parse a json object value.
*/
void parse_object() {
assert(states.top() >= VALUE_OBJECT && states.top() <= OBJECT_VALUE);

map<string, Json> data;

set_state(OBJECT_KEY_OR_END);
char ch = get_next_token();
if (ch == '}')
if (ch == '}') {
pop_state();
return values.push(data);
}

while (1) {
if (need_data)
Expand All @@ -754,6 +824,8 @@ struct JsonParserPriv final {
if (ch != '"')
return fail("expected '\"' in object, got " + esc(ch));

set_state(OBJECT_KEY);
push_state(VALUE_STRING);
parse_string();
if (need_data)
return;
Expand All @@ -764,13 +836,16 @@ struct JsonParserPriv final {
if (failed)
return values.push(Json());

set_state(OBJECT_COLON);
ch = get_next_token();
if (need_data)
return;

if (ch != ':')
return fail("expected ':' in object, got " + esc(ch));

set_state(OBJECT_VALUE);
push_state(EXPECT_VALUE);
parse_json();
if (need_data)
return;
Expand All @@ -783,12 +858,14 @@ struct JsonParserPriv final {

data[std::move(key)] = value;

set_state(OBJECT_COMMA_OR_END);
ch = get_next_token();
if (need_data)
return;

if (ch == '}') {
values.push(data);
pop_state();
break;
}

Expand All @@ -804,17 +881,27 @@ struct JsonParserPriv final {
* Parse a json array value.
*/
void parse_array() {
assert(states.top() >= VALUE_ARRAY && states.top() <= ARRAY_VALUE);

vector<Json> data;

set_state(ARRAY_VALUE_OR_END);
char ch = get_next_token();

if (ch == ']')
if (ch == ']') {
pop_state();
return values.push(data);
}

while (1) {
if (need_data)
return;

i--;

set_state(ARRAY_VALUE);
push_state(EXPECT_VALUE);

parse_json();
if (need_data)
return;
Expand All @@ -826,12 +913,14 @@ struct JsonParserPriv final {
return values.push(Json());
data.push_back(value);

set_state(ARRAY_COMMA_OR_END);
ch = get_next_token();
if (need_data)
return;

if (ch == ']') {
values.push(data);
pop_state();
break;
}

Expand All @@ -848,6 +937,8 @@ struct JsonParserPriv final {
* Parse any JSON value.
*/
void parse_json() {
assert_state(EXPECT_VALUE);

if (values.size() > max_depth) {
return fail("exceeded maximum nesting depth");
}
Expand All @@ -858,26 +949,39 @@ struct JsonParserPriv final {

if (ch == '-' || (ch >= '0' && ch <= '9')) {
i--;
set_state(VALUE_NUMBER);
return parse_number();
}

if (ch == 't')
if (ch == 't') {
set_state(VALUE_TRUE);
return parse_true();
}

if (ch == 'f')
if (ch == 'f') {
set_state(VALUE_FALSE);
return parse_false();
}

if (ch == 'n')
if (ch == 'n') {
set_state(VALUE_NULL);
return parse_null();
}

if (ch == '"')
if (ch == '"') {
set_state(VALUE_STRING);
return parse_string();
}

if (ch == '{')
if (ch == '{') {
set_state(VALUE_OBJECT);
return parse_object();
}

if (ch == '[')
if (ch == '[') {
set_state(VALUE_ARRAY);
return parse_array();
}

return fail("expected value, got " + esc(ch));
}
Expand Down Expand Up @@ -906,13 +1010,15 @@ Json JsonParser::json() const {

Json Json::parse(const string &in, string &err, JsonParse strategy) {
JsonParserPriv parser { in, err, strategy };
assert(parser.states.size() == 1);

parser.eof = true;
parser.parse_json();

// Check for any trailing garbage
parser.consume_garbage();
if (parser.i != in.size()) {
parser.fail("unexpected trailing " + esc(in[parser.i]));
err = "unexpected trailing " + esc(in[parser.i]);
return Json();
}

Expand All @@ -923,8 +1029,10 @@ Json Json::parse(const string &in, string &err, JsonParse strategy) {
}

#ifndef NDEBUG
if (!parser.failed)
if (!parser.failed) {
assert(parser.values.size() == 1);
assert(parser.states.empty());
}
#endif
return parser.values.top();
}
Expand Down Expand Up @@ -952,8 +1060,11 @@ vector<Json> Json::parse_multi(const string &in,
parser.values.pop();
// Check for another object
parser.consume_garbage();
if (!parser.failed && !parser.need_data)
if (!parser.failed && !parser.need_data) {
assert(parser.states.empty());
parser.push_state(JsonParserPriv::EXPECT_VALUE);
parser_stop_pos = parser.i;
}
}
return json_vec;
}
Expand Down

0 comments on commit 621d48c

Please sign in to comment.