static guint json_parse_statement (JsonParser *parser, JsonScanner *scanner) { JsonParserPrivate *priv = parser->priv; guint token; token = json_scanner_peek_next_token (scanner); switch (token) { case G_TOKEN_LEFT_CURLY: JSON_NOTE (PARSER, "Statement is object declaration"); return json_parse_object (parser, scanner, &priv->root); case G_TOKEN_LEFT_BRACE: JSON_NOTE (PARSER, "Statement is array declaration"); return json_parse_array (parser, scanner, &priv->root); /* some web APIs are not only passing the data structures: they are * also passing an assigment, which makes parsing horribly complicated * only because web developers are lazy, and writing "var foo = " is * evidently too much to request from them. */ case JSON_TOKEN_VAR: { guint next_token; gchar *name; JSON_NOTE (PARSER, "Statement is an assignment"); /* swallow the 'var' token... */ token = json_scanner_get_next_token (scanner); /* ... swallow the variable name... */ next_token = json_scanner_get_next_token (scanner); if (next_token != G_TOKEN_IDENTIFIER) { priv->error_code = JSON_PARSER_ERROR_INVALID_BAREWORD; return G_TOKEN_IDENTIFIER; } name = g_strdup (scanner->value.v_identifier); /* ... and finally swallow the '=' */ next_token = json_scanner_get_next_token (scanner); if (next_token != '=') { priv->error_code = JSON_PARSER_ERROR_INVALID_BAREWORD; g_free (name); return '='; } priv->has_assignment = TRUE; priv->variable_name = name; token = json_parse_statement (parser, scanner); /* remove the trailing semi-colon */ next_token = json_scanner_peek_next_token (scanner); if (next_token == ';') { token = json_scanner_get_next_token (scanner); return G_TOKEN_NONE; } return token; } break; case JSON_TOKEN_NULL: case JSON_TOKEN_TRUE: case JSON_TOKEN_FALSE: case '-': case G_TOKEN_INT: case G_TOKEN_FLOAT: case G_TOKEN_STRING: case G_TOKEN_IDENTIFIER: JSON_NOTE (PARSER, "Statement is a value"); token = json_scanner_get_next_token (scanner); return json_parse_value (parser, scanner, token, &priv->root); default: JSON_NOTE (PARSER, "Unknown statement"); json_scanner_get_next_token (scanner); priv->error_code = JSON_PARSER_ERROR_INVALID_BAREWORD; return G_TOKEN_SYMBOL; } }
static gboolean json_parser_load (JsonParser *parser, const gchar *data, gsize length, GError **error) { JsonParserPrivate *priv = parser->priv; JsonScanner *scanner; gboolean done; gboolean retval = TRUE; gint i; json_parser_clear (parser); if (!g_utf8_validate (data, -1, NULL)) { g_set_error_literal (error, JSON_PARSER_ERROR, JSON_PARSER_ERROR_INVALID_DATA, _("JSON data must be UTF-8 encoded")); g_signal_emit (parser, parser_signals[ERROR], 0, *error); return FALSE; } scanner = json_scanner_create (parser); json_scanner_input_text (scanner, data, length); priv->scanner = scanner; g_signal_emit (parser, parser_signals[PARSE_START], 0); done = FALSE; while (!done) { if (json_scanner_peek_next_token (scanner) == G_TOKEN_EOF) done = TRUE; else { guint expected_token; gint cur_token; /* we try to show the expected token, if possible */ expected_token = json_parse_statement (parser, scanner); if (expected_token != G_TOKEN_NONE) { const gchar *symbol_name; gchar *msg; cur_token = scanner->token; msg = NULL; symbol_name = NULL; if (scanner->scope_id == 0) { if (expected_token > JSON_TOKEN_INVALID && expected_token < JSON_TOKEN_LAST) { for (i = 0; i < n_symbols; i++) if (symbols[i].token == expected_token) symbol_name = symbol_names + symbols[i].name_offset; if (!msg) msg = g_strconcat ("e.g. '", symbol_name, "'", NULL); } if (cur_token > JSON_TOKEN_INVALID && cur_token < JSON_TOKEN_LAST) { symbol_name = "???"; for (i = 0; i < n_symbols; i++) if (symbols[i].token == cur_token) symbol_name = symbol_names + symbols[i].name_offset; } } /* this will emit the ::error signal via the custom * message handler we install */ json_scanner_unexp_token (scanner, expected_token, NULL, "value", symbol_name, msg); /* and this will propagate the error we create in the * same message handler */ if (priv->last_error) { g_propagate_error (error, priv->last_error); priv->last_error = NULL; } retval = FALSE; g_free (msg); done = TRUE; } } } g_signal_emit (parser, parser_signals[PARSE_END], 0); /* remove the scanner */ json_scanner_destroy (scanner); priv->scanner = NULL; priv->current_node = NULL; return retval; }
static guint json_parse_array (JsonParser *parser, JsonScanner *scanner, JsonNode **node) { JsonParserPrivate *priv = parser->priv; JsonNode *old_current; JsonArray *array; guint token; gint idx; old_current = priv->current_node; priv->current_node = json_node_init_array (json_node_alloc (), NULL); array = json_array_new (); token = json_scanner_get_next_token (scanner); g_assert (token == G_TOKEN_LEFT_BRACE); g_signal_emit (parser, parser_signals[ARRAY_START], 0); idx = 0; while (token != G_TOKEN_RIGHT_BRACE) { guint next_token = json_scanner_peek_next_token (scanner); JsonNode *element = NULL; /* parse the element */ switch (next_token) { case G_TOKEN_LEFT_BRACE: JSON_NOTE (PARSER, "Nested array at index %d", idx); token = json_parse_array (parser, scanner, &element); break; case G_TOKEN_LEFT_CURLY: JSON_NOTE (PARSER, "Nested object at index %d", idx); token = json_parse_object (parser, scanner, &element); break; case G_TOKEN_RIGHT_BRACE: goto array_done; default: token = json_scanner_get_next_token (scanner); token = json_parse_value (parser, scanner, token, &element); break; } if (token != G_TOKEN_NONE || element == NULL) { /* the json_parse_* functions will have set the error code */ json_array_unref (array); json_node_free (priv->current_node); priv->current_node = old_current; return token; } next_token = json_scanner_peek_next_token (scanner); if (next_token == G_TOKEN_COMMA) { token = json_scanner_get_next_token (scanner); next_token = json_scanner_peek_next_token (scanner); /* look for trailing commas */ if (next_token == G_TOKEN_RIGHT_BRACE) { priv->error_code = JSON_PARSER_ERROR_TRAILING_COMMA; json_array_unref (array); json_node_free (priv->current_node); json_node_free (element); priv->current_node = old_current; return G_TOKEN_RIGHT_BRACE; } } JSON_NOTE (PARSER, "Array element %d completed", idx + 1); json_node_set_parent (element, priv->current_node); json_array_add_element (array, element); g_signal_emit (parser, parser_signals[ARRAY_ELEMENT], 0, array, idx); token = next_token; } array_done: json_scanner_get_next_token (scanner); json_node_take_array (priv->current_node, array); json_node_set_parent (priv->current_node, old_current); g_signal_emit (parser, parser_signals[ARRAY_END], 0, array); if (node != NULL && *node == NULL) *node = priv->current_node; priv->current_node = old_current; return G_TOKEN_NONE; }
static guint json_parse_object (JsonParser *parser, JsonScanner *scanner, JsonNode **node) { JsonParserPrivate *priv = parser->priv; JsonObject *object; JsonNode *old_current; guint token; old_current = priv->current_node; priv->current_node = json_node_init_object (json_node_alloc (), NULL); object = json_object_new (); token = json_scanner_get_next_token (scanner); g_assert (token == G_TOKEN_LEFT_CURLY); g_signal_emit (parser, parser_signals[OBJECT_START], 0); while (token != G_TOKEN_RIGHT_CURLY) { guint next_token = json_scanner_peek_next_token (scanner); JsonNode *member = NULL; gchar *name; /* we need to abort here because empty objects do not * have member names */ if (next_token == G_TOKEN_RIGHT_CURLY) break; /* parse the member's name */ if (next_token != G_TOKEN_STRING) { JSON_NOTE (PARSER, "Missing object member name"); priv->error_code = JSON_PARSER_ERROR_INVALID_BAREWORD; json_object_unref (object); json_node_free (priv->current_node); priv->current_node = old_current; return G_TOKEN_STRING; } /* member name */ token = json_scanner_get_next_token (scanner); name = g_strdup (scanner->value.v_string); if (name == NULL || *name == '\0') { JSON_NOTE (PARSER, "Empty object member name"); priv->error_code = JSON_PARSER_ERROR_EMPTY_MEMBER_NAME; json_object_unref (object); json_node_free (priv->current_node); priv->current_node = old_current; return G_TOKEN_STRING; } JSON_NOTE (PARSER, "Object member '%s'", name); /* a colon separates names from values */ next_token = json_scanner_peek_next_token (scanner); if (next_token != ':') { JSON_NOTE (PARSER, "Missing object member name separator"); priv->error_code = JSON_PARSER_ERROR_MISSING_COLON; g_free (name); json_object_unref (object); json_node_free (priv->current_node); priv->current_node = old_current; return ':'; } /* we swallow the ':' */ token = json_scanner_get_next_token (scanner); g_assert (token == ':'); next_token = json_scanner_peek_next_token (scanner); /* parse the member's value */ switch (next_token) { case G_TOKEN_LEFT_BRACE: JSON_NOTE (PARSER, "Nested array at member %s", name); token = json_parse_array (parser, scanner, &member); break; case G_TOKEN_LEFT_CURLY: JSON_NOTE (PARSER, "Nested object at member %s", name); token = json_parse_object (parser, scanner, &member); break; default: /* once a member name is defined we need a value */ token = json_scanner_get_next_token (scanner); token = json_parse_value (parser, scanner, token, &member); break; } if (token != G_TOKEN_NONE || member == NULL) { /* the json_parse_* functions will have set the error code */ g_free (name); json_object_unref (object); json_node_free (priv->current_node); priv->current_node = old_current; return token; } next_token = json_scanner_peek_next_token (scanner); if (next_token == G_TOKEN_COMMA) { token = json_scanner_get_next_token (scanner); next_token = json_scanner_peek_next_token (scanner); /* look for trailing commas */ if (next_token == G_TOKEN_RIGHT_CURLY) { priv->error_code = JSON_PARSER_ERROR_TRAILING_COMMA; json_object_unref (object); json_node_free (member); json_node_free (priv->current_node); priv->current_node = old_current; return G_TOKEN_RIGHT_BRACE; } } else if (next_token == G_TOKEN_STRING) { priv->error_code = JSON_PARSER_ERROR_MISSING_COMMA; json_object_unref (object); json_node_free (member); json_node_free (priv->current_node); priv->current_node = old_current; return G_TOKEN_COMMA; } JSON_NOTE (PARSER, "Object member '%s' completed", name); json_node_set_parent (member, priv->current_node); json_object_set_member (object, name, member); g_signal_emit (parser, parser_signals[OBJECT_MEMBER], 0, object, name); g_free (name); token = next_token; } json_scanner_get_next_token (scanner); json_node_take_object (priv->current_node, object); json_node_set_parent (priv->current_node, old_current); g_signal_emit (parser, parser_signals[OBJECT_END], 0, object); if (node != NULL && *node == NULL) *node = priv->current_node; priv->current_node = old_current; return G_TOKEN_NONE; }
static guint json_parse_value (JsonParser *parser, JsonScanner *scanner, guint token, JsonNode **node) { JsonParserPrivate *priv = parser->priv; JsonNode *current_node = priv->current_node; gboolean is_negative = FALSE; if (token == '-') { guint next_token = json_scanner_peek_next_token (scanner); if (next_token == G_TOKEN_INT || next_token == G_TOKEN_FLOAT) { is_negative = TRUE; token = json_scanner_get_next_token (scanner); } else return G_TOKEN_INT; } switch (token) { case G_TOKEN_INT: JSON_NOTE (PARSER, "abs(node): %" G_GINT64_FORMAT " (sign: %s)", scanner->value.v_int64, is_negative ? "negative" : "positive"); *node = json_node_init_int (json_node_alloc (), is_negative ? scanner->value.v_int64 * -1 : scanner->value.v_int64); break; case G_TOKEN_FLOAT: JSON_NOTE (PARSER, "abs(node): %.6f (sign: %s)", scanner->value.v_float, is_negative ? "negative" : "positive"); *node = json_node_init_double (json_node_alloc (), is_negative ? scanner->value.v_float * -1.0 : scanner->value.v_float); break; case G_TOKEN_STRING: JSON_NOTE (PARSER, "node: '%s'", scanner->value.v_string); *node = json_node_init_string (json_node_alloc (), scanner->value.v_string); break; case JSON_TOKEN_TRUE: case JSON_TOKEN_FALSE: JSON_NOTE (PARSER, "node: '%s'", JSON_TOKEN_TRUE ? "<true>" : "<false>"); *node = json_node_init_boolean (json_node_alloc (), token == JSON_TOKEN_TRUE ? TRUE : FALSE); break; case JSON_TOKEN_NULL: JSON_NOTE (PARSER, "node: <null>"); *node = json_node_init_null (json_node_alloc ()); break; case G_TOKEN_IDENTIFIER: JSON_NOTE (PARSER, "node: identifier '%s'", scanner->value.v_identifier); priv->error_code = JSON_PARSER_ERROR_INVALID_BAREWORD; *node = NULL; return G_TOKEN_SYMBOL; default: { JsonNodeType cur_type; *node = NULL; JSON_NOTE (PARSER, "node: invalid token"); cur_type = json_node_get_node_type (current_node); if (cur_type == JSON_NODE_ARRAY) { priv->error_code = JSON_PARSER_ERROR_PARSE; return G_TOKEN_RIGHT_BRACE; } else if (cur_type == JSON_NODE_OBJECT) { priv->error_code = JSON_PARSER_ERROR_PARSE; return G_TOKEN_RIGHT_CURLY; } else { priv->error_code = JSON_PARSER_ERROR_INVALID_BAREWORD; return G_TOKEN_SYMBOL; } } break; } return G_TOKEN_NONE; }
static guint json_parse_statement (JsonParser *parser, JsonScanner *scanner) { JsonParserPrivate *priv = parser->priv; guint token; token = json_scanner_peek_next_token (scanner); switch (token) { case G_TOKEN_LEFT_CURLY: return json_parse_object (parser, scanner, &priv->root); case G_TOKEN_LEFT_BRACE: return json_parse_array (parser, scanner, &priv->root); /* some web APIs are not only passing the data structures: they are * also passing an assigment, which makes parsing horribly complicated * only because web developers are lazy, and writing "var foo = " is * evidently too much to request from them. */ case JSON_TOKEN_VAR: { guint next_token; gchar *name; /* swallow the 'var' token... */ token = json_scanner_get_next_token (scanner); /* ... swallow the variable name... */ next_token = json_scanner_get_next_token (scanner); if (next_token != G_TOKEN_IDENTIFIER) return G_TOKEN_IDENTIFIER; name = g_strdup (scanner->value.v_identifier); /* ... and finally swallow the '=' */ next_token = json_scanner_get_next_token (scanner); if (next_token != '=') return '='; priv->has_assignment = TRUE; priv->variable_name = name; token = json_parse_statement (parser, scanner); /* remove the trailing semi-colon */ next_token = json_scanner_peek_next_token (scanner); if (next_token == ';') { token = json_scanner_get_next_token (scanner); return G_TOKEN_NONE; } return token; } break; case JSON_TOKEN_NULL: priv->root = priv->current_node = json_node_new (JSON_NODE_NULL); json_scanner_get_next_token (scanner); return G_TOKEN_NONE; case JSON_TOKEN_TRUE: case JSON_TOKEN_FALSE: priv->root = priv->current_node = json_node_new (JSON_NODE_VALUE); json_node_set_boolean (priv->current_node, token == JSON_TOKEN_TRUE ? TRUE : FALSE); json_scanner_get_next_token (scanner); return G_TOKEN_NONE; case '-': { guint next_token; token = json_scanner_get_next_token (scanner); next_token = json_scanner_peek_next_token (scanner); if (next_token == G_TOKEN_INT || next_token == G_TOKEN_FLOAT) { priv->root = priv->current_node = json_node_new (JSON_NODE_VALUE); token = json_scanner_get_next_token (scanner); switch (token) { case G_TOKEN_INT: json_node_set_int (priv->current_node, scanner->value.v_int64 * -1); break; case G_TOKEN_FLOAT: json_node_set_double (priv->current_node, scanner->value.v_float * -1.0); break; default: return G_TOKEN_INT; } json_scanner_get_next_token (scanner); return G_TOKEN_NONE; } else return G_TOKEN_INT; } break; case G_TOKEN_INT: case G_TOKEN_FLOAT: case G_TOKEN_STRING: token = json_scanner_get_next_token (scanner); return json_parse_value (parser, scanner, token, &priv->root); default: json_scanner_get_next_token (scanner); return G_TOKEN_SYMBOL; } }
static guint json_parse_object (JsonParser *parser, JsonScanner *scanner, JsonNode **node) { JsonParserPrivate *priv = parser->priv; JsonObject *object; JsonNode *old_current; guint token; old_current = priv->current_node; priv->current_node = json_node_new (JSON_NODE_OBJECT); object = json_object_new (); token = json_scanner_get_next_token (scanner); g_assert (token == G_TOKEN_LEFT_CURLY); g_signal_emit (parser, parser_signals[OBJECT_START], 0); while (token != G_TOKEN_RIGHT_CURLY) { guint next_token = json_scanner_peek_next_token (scanner); JsonNode *member = NULL; gchar *name; /* we need to abort here because empty objects do not * have member names */ if (next_token == G_TOKEN_RIGHT_CURLY) break; /* parse the member's name */ if (next_token != G_TOKEN_STRING) { json_object_unref (object); json_node_free (priv->current_node); priv->current_node = old_current; return G_TOKEN_STRING; } /* member name */ token = json_scanner_get_next_token (scanner); name = g_strdup (scanner->value.v_string); /* a colon separates names from values */ next_token = json_scanner_peek_next_token (scanner); if (next_token != ':') { g_free (name); json_object_unref (object); json_node_free (priv->current_node); priv->current_node = old_current; return ':'; } /* we swallow the ':' */ token = json_scanner_get_next_token (scanner); g_assert (token == ':'); next_token = json_scanner_peek_next_token (scanner); /* parse the member's value */ switch (next_token) { case G_TOKEN_LEFT_BRACE: token = json_parse_array (parser, scanner, &member); break; case G_TOKEN_LEFT_CURLY: token = json_parse_object (parser, scanner, &member); break; case G_TOKEN_INT: case G_TOKEN_FLOAT: case G_TOKEN_STRING: case '-': case JSON_TOKEN_TRUE: case JSON_TOKEN_FALSE: case JSON_TOKEN_NULL: token = json_scanner_get_next_token (scanner); token = json_parse_value (parser, scanner, token, &member); break; default: /* once a member name is defined we need a value */ token = G_TOKEN_SYMBOL; break; } if (token != G_TOKEN_NONE || member == NULL) { /* the json_parse_* functions will have set the error code */ g_free (name); json_object_unref (object); json_node_free (priv->current_node); priv->current_node = old_current; return token; } next_token = json_scanner_peek_next_token (scanner); if (next_token == G_TOKEN_COMMA) { token = json_scanner_get_next_token (scanner); next_token = json_scanner_peek_next_token (scanner); /* look for trailing commas */ if (next_token == G_TOKEN_RIGHT_CURLY) { json_object_unref (object); json_node_free (member); json_node_free (priv->current_node); priv->current_node = old_current; return G_TOKEN_RIGHT_BRACE; } } else if (next_token == G_TOKEN_STRING) { json_object_unref (object); json_node_free (member); json_node_free (priv->current_node); priv->current_node = old_current; return G_TOKEN_COMMA; } json_node_set_parent (member, priv->current_node); json_object_set_member (object, name, member); g_signal_emit (parser, parser_signals[OBJECT_MEMBER], 0, object, name); g_free (name); token = next_token; } json_scanner_get_next_token (scanner); json_node_take_object (priv->current_node, object); json_node_set_parent (priv->current_node, old_current); g_signal_emit (parser, parser_signals[OBJECT_END], 0, object); if (node != NULL && *node == NULL) *node = priv->current_node; priv->current_node = old_current; return G_TOKEN_NONE; }
static guint json_parse_array (JsonParser *parser, JsonScanner *scanner, JsonNode **node) { JsonParserPrivate *priv = parser->priv; JsonNode *old_current; JsonArray *array; guint token; gint idx; old_current = priv->current_node; priv->current_node = json_node_new (JSON_NODE_ARRAY); array = json_array_new (); token = json_scanner_get_next_token (scanner); g_assert (token == G_TOKEN_LEFT_BRACE); g_signal_emit (parser, parser_signals[ARRAY_START], 0); idx = 0; while (token != G_TOKEN_RIGHT_BRACE) { guint next_token = json_scanner_peek_next_token (scanner); JsonNode *element = NULL; /* parse the element */ switch (next_token) { case G_TOKEN_LEFT_BRACE: token = json_parse_array (parser, scanner, &element); break; case G_TOKEN_LEFT_CURLY: token = json_parse_object (parser, scanner, &element); break; case G_TOKEN_INT: case G_TOKEN_FLOAT: case G_TOKEN_STRING: case '-': case JSON_TOKEN_TRUE: case JSON_TOKEN_FALSE: case JSON_TOKEN_NULL: token = json_scanner_get_next_token (scanner); token = json_parse_value (parser, scanner, token, &element); break; case G_TOKEN_RIGHT_BRACE: goto array_done; default: if (next_token != G_TOKEN_RIGHT_BRACE) token = G_TOKEN_RIGHT_BRACE; break; } if (token != G_TOKEN_NONE || element == NULL) { /* the json_parse_* functions will have set the error code */ json_array_unref (array); json_node_free (priv->current_node); priv->current_node = old_current; return token; } next_token = json_scanner_peek_next_token (scanner); if (next_token == G_TOKEN_COMMA) { token = json_scanner_get_next_token (scanner); next_token = json_scanner_peek_next_token (scanner); /* look for trailing commas */ if (next_token == G_TOKEN_RIGHT_BRACE) { json_array_unref (array); json_node_free (priv->current_node); json_node_free (element); priv->current_node = old_current; return G_TOKEN_RIGHT_BRACE; } } json_node_set_parent (element, priv->current_node); json_array_add_element (array, element); g_signal_emit (parser, parser_signals[ARRAY_ELEMENT], 0, array, idx); token = next_token; } array_done: json_scanner_get_next_token (scanner); json_node_take_array (priv->current_node, array); json_node_set_parent (priv->current_node, old_current); g_signal_emit (parser, parser_signals[ARRAY_END], 0, array); if (node != NULL && *node == NULL) *node = priv->current_node; priv->current_node = old_current; return G_TOKEN_NONE; }
static guint json_parse_value (JsonParser *parser, JsonScanner *scanner, guint token, JsonNode **node) { JsonNode *current_node = parser->priv->current_node; gboolean is_negative = FALSE; if (token == '-') { guint next_token = json_scanner_peek_next_token (scanner); if (next_token == G_TOKEN_INT || next_token == G_TOKEN_FLOAT) { is_negative = TRUE; token = json_scanner_get_next_token (scanner); } else return G_TOKEN_INT; } switch (token) { case G_TOKEN_INT: *node = json_node_new (JSON_NODE_VALUE); json_node_set_int (*node, is_negative ? scanner->value.v_int64 * -1 : scanner->value.v_int64); break; case G_TOKEN_FLOAT: *node = json_node_new (JSON_NODE_VALUE); json_node_set_double (*node, is_negative ? scanner->value.v_float * -1.0 : scanner->value.v_float); break; case G_TOKEN_STRING: *node = json_node_new (JSON_NODE_VALUE); json_node_set_string (*node, scanner->value.v_string); break; case JSON_TOKEN_TRUE: case JSON_TOKEN_FALSE: *node = json_node_new (JSON_NODE_VALUE); json_node_set_boolean (*node, token == JSON_TOKEN_TRUE ? TRUE : FALSE); break; case JSON_TOKEN_NULL: *node = json_node_new (JSON_NODE_NULL); break; default: { JsonNodeType cur_type; *node = NULL; cur_type = json_node_get_node_type (current_node); if (cur_type == JSON_NODE_ARRAY) return G_TOKEN_RIGHT_BRACE; else if (cur_type == JSON_NODE_OBJECT) return G_TOKEN_RIGHT_CURLY; else return G_TOKEN_SYMBOL; } } return G_TOKEN_NONE; }