summaryrefslogtreecommitdiff
path: root/json-glib/json-parser.c
diff options
context:
space:
mode:
authorEmmanuele Bassi <ebassi@openedhand.com>2007-09-21 11:54:40 +0100
committerEmmanuele Bassi <ebassi@openedhand.com>2007-09-21 11:54:40 +0100
commitd98d8c3d245192abe6ec5a531c9d0d678b07d061 (patch)
tree8e30d2694dc9f9ac94c3973fd9fd7276199445c1 /json-glib/json-parser.c
parentcd1040e2fb6f6da50aaf72017746b33234c39704 (diff)
downloadjson-glib-d98d8c3d245192abe6ec5a531c9d0d678b07d061.tar.gz
Add stubs to JsonParser for actually parsing a JSON stream
Initial commit for getting JsonParser to work. Because GScanner API is old and mostly sucks, we need to do some magic with signals. If json_parser_load_from_data() fails, the passed GError will be set with a JSON_PARSER_ERROR code and message; unfortunately, we can't get the nice error message out of GScanner. We can, however, override the default message handler and make it emit a signal on the JsonParser object. So, to make a long story short: the GError passed to the load_from_data() method is filled with a short error message; the *real* error message is passed to the ::error signal handlers so they can actually use it. GScanner should really get a way to retrieve the last error message.
Diffstat (limited to 'json-glib/json-parser.c')
-rw-r--r--json-glib/json-parser.c266
1 files changed, 265 insertions, 1 deletions
diff --git a/json-glib/json-parser.c b/json-glib/json-parser.c
index f44451f..dbdefd3 100644
--- a/json-glib/json-parser.c
+++ b/json-glib/json-parser.c
@@ -2,6 +2,7 @@
#include <string.h>
+#include "json-marshal.h"
#include "json-parser.h"
#include "json-private.h"
@@ -19,6 +20,71 @@ struct _JsonParserPrivate
GList *top_levels;
};
+static const GScannerConfig json_scanner_config =
+{
+ ( " \t\r\n" ) /* cset_skip_characters */,
+ (
+ "_"
+ G_CSET_a_2_z
+ G_CSET_A_2_Z
+ ) /* cset_identifier_first */,
+ (
+ G_CSET_DIGITS
+ "-_"
+ G_CSET_a_2_z
+ G_CSET_A_2_Z
+ ) /* cset_identifier_nth */,
+ ( "//\n" ) /* cpair_comment_single */,
+ TRUE /* case_sensitive */,
+ TRUE /* skip_comment_multi */,
+ TRUE /* skip_comment_single */,
+ TRUE /* scan_comment_multi */,
+ TRUE /* scan_identifier */,
+ FALSE /* scan_identifier_1char */,
+ FALSE /* scan_identifier_NULL */,
+ TRUE /* scan_symbols */,
+ TRUE /* scan_binary */,
+ TRUE /* scan_octal */,
+ TRUE /* scan_float */,
+ TRUE /* scan_hex */,
+ TRUE /* scan_hex_dollar */,
+ TRUE /* scan_string_sq */,
+ TRUE /* scan_string_dq */,
+ TRUE /* numbers_2_int */,
+ FALSE /* int_2_float */,
+ FALSE /* identifier_2_string */,
+ TRUE /* char_2_token */,
+ TRUE /* symbol_2_token */,
+ FALSE /* scope_0_fallback */,
+};
+
+
+static const gchar symbol_names[] =
+ "true\0"
+ "false\0"
+ "null\0";
+
+static const struct
+{
+ guint name_offset;
+ guint token;
+} symbols[] = {
+ { 0, JSON_TOKEN_TRUE },
+ { 5, JSON_TOKEN_FALSE },
+ { 11, JSON_TOKEN_NULL }
+};
+
+static const guint n_symbols = G_N_ELEMENTS (symbols);
+
+enum
+{
+ ERROR,
+
+ LAST_SIGNAL
+};
+
+static guint parser_signals[LAST_SIGNAL] = { 0, };
+
G_DEFINE_TYPE (JsonParser, json_parser, G_TYPE_OBJECT);
static void
@@ -35,6 +101,24 @@ json_parser_class_init (JsonParserClass *klass)
g_type_class_add_private (klass, sizeof (JsonParserPrivate));
gobject_class->dispose = json_parser_dispose;
+
+ /**
+ * JsonParser::error:
+ * @parser: the parser instance that received the signal
+ * @error: a pointer to the #GError
+ *
+ * The ::error signal is emitted each time a #JsonParser encounters
+ * an error in a JSON stream.
+ */
+ parser_signals[ERROR] =
+ g_signal_new ("error",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (JsonParserClass, error),
+ NULL, NULL,
+ _json_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
}
static void
@@ -47,6 +131,104 @@ json_parser_init (JsonParser *parser)
priv->top_levels = NULL;
}
+static guint
+json_parse_array (JsonParser *parser,
+ GScanner *scanner)
+{
+ return G_TOKEN_NONE;
+}
+
+static guint
+json_parse_pair (JsonParser *parse,
+ GScanner *scanner)
+{
+ return G_TOKEN_NONE;
+}
+
+static guint
+json_parse_object (JsonParser *parser,
+ GScanner *scanner)
+{
+ guint token;
+ guint result = G_TOKEN_NONE;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token != G_TOKEN_LEFT_CURLY)
+ return G_TOKEN_LEFT_CURLY;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token == G_TOKEN_RIGHT_CURLY)
+ return G_TOKEN_NONE;
+ else
+ {
+ }
+
+ return result;
+}
+
+static guint
+json_parse_statement (JsonParser *parser,
+ GScanner *scanner)
+{
+ guint token;
+
+ token = g_scanner_peek_next_token (scanner);
+ switch (token)
+ {
+ case G_TOKEN_LEFT_CURLY:
+ return json_parse_object (parser, scanner);
+
+ case G_TOKEN_LEFT_BRACE:
+ return json_parse_array (parser, scanner);
+
+ case G_TOKEN_STRING:
+ return json_parse_pair (parser, scanner);
+
+ default:
+ g_scanner_get_next_token (scanner);
+ return G_TOKEN_SYMBOL;
+ }
+}
+
+static void
+json_scanner_msg_handler (GScanner *scanner,
+ gchar *message,
+ gboolean error)
+{
+ JsonParser *parser = scanner->user_data;
+
+ if (error)
+ {
+ GError *error = NULL;
+
+ g_set_error (&error, JSON_PARSER_ERROR,
+ JSON_PARSER_ERROR_PARSE,
+ "Parse error on line %d: %s",
+ scanner->line,
+ message);
+
+ g_signal_emit (parser, parser_signals[ERROR], 0, error);
+
+ g_error_free (error);
+ }
+ else
+ {
+ g_warning ("Line %d: %s", scanner->line, message);
+ }
+}
+
+static GScanner *
+json_scanner_new (JsonParser *parser)
+{
+ GScanner *scanner;
+
+ scanner = g_scanner_new (&json_scanner_config);
+ scanner->msg_handler = json_scanner_msg_handler;
+ scanner->user_data = parser;
+
+ return scanner;
+}
+
/**
* json_parser_new:
*
@@ -123,13 +305,95 @@ json_parser_load_from_data (JsonParser *parser,
gsize length,
GError **error)
{
+ GScanner *scanner;
+ gboolean done = FALSE;
+ gboolean retval = TRUE;
+ gint i;
+
g_return_val_if_fail (JSON_IS_PARSER (parser), FALSE);
g_return_val_if_fail (data != NULL, FALSE);
if (length < 0)
length = strlen (data);
- return TRUE;
+ scanner = json_scanner_new (parser);
+ g_scanner_input_text (scanner, data, length);
+
+ for (i = 0; i < n_symbols; i++)
+ {
+ g_scanner_scope_add_symbol (scanner, 0,
+ symbol_names + symbols[i].name_offset,
+ GINT_TO_POINTER (symbols[i].token));
+ }
+
+ while (!done)
+ {
+ if (g_scanner_peek_next_token (scanner) == G_TOKEN_EOF)
+ done = TRUE;
+ else
+ {
+ guint expected_token;
+
+ expected_token = json_parse_statement (parser, scanner);
+ if (expected_token != G_TOKEN_NONE)
+ {
+ const gchar *symbol_name;
+ gchar *msg;
+
+ 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)
+ msg = (gchar *) symbol_names + symbols[i].name_offset;
+
+ if (msg)
+ msg = g_strconcat ("e.g. `", msg, "'", NULL);
+ }
+
+ if (scanner->token > JSON_TOKEN_INVALID &&
+ scanner->token < JSON_TOKEN_LAST)
+ {
+ symbol_name = "???";
+
+ for (i = 0; i < n_symbols; i++)
+ if (symbols[i].token == scanner->token)
+ symbol_name = symbol_names + symbols[i].name_offset;
+ }
+ }
+
+ /* this will emit the ::error signal via the custom
+ * message handler we install
+ */
+ g_scanner_unexp_token (scanner, expected_token,
+ NULL, "keyword",
+ symbol_name, msg,
+ TRUE);
+
+ /* we set a generic error here; the message from
+ * GScanner is relayed in the ::error signal
+ */
+ g_set_error (error, JSON_PARSER_ERROR,
+ JSON_PARSER_ERROR_PARSE,
+ "Invalid token `%s' found: expecting %s",
+ symbol_name,
+ msg);
+
+ retval = FALSE;
+
+ g_free (msg);
+ done = TRUE;
+ }
+ }
+ }
+
+ g_scanner_destroy (scanner);
+
+ return retval;
}
/**