[glib/gsettings] Parser improvements



commit 76a2aed2b0fa5fcd83597262ac5f046d9d435ddc
Author: Ryan Lortie <desrt desrt ca>
Date:   Wed Aug 26 12:20:18 2009 -0400

    Parser improvements

 glib/gvariant-parser.c |  744 ++++++++++++++++++++++++++----------------------
 glib/gvariant.h        |   21 +-
 2 files changed, 408 insertions(+), 357 deletions(-)
---
diff --git a/glib/gvariant-parser.c b/glib/gvariant-parser.c
index 297871f..a577e73 100644
--- a/glib/gvariant-parser.c
+++ b/glib/gvariant-parser.c
@@ -25,31 +25,54 @@
 
 typedef struct
 {
+  GVariantParseError error;
+
   const gchar *stream;
-  gchar *this;
+  const gchar *end;
+
+  const gchar *this;
 } TokenStream;
 
 static void
+set_error (GVariantParseError *error,
+           const gchar        *start,
+           const gchar        *end,
+           const gchar        *message)
+{
+  g_assert (error->error == NULL);
+
+  error->error = g_strdup (message);
+  error->start = start;
+  error->end = end;
+}
+
+static void
 token_stream_prepare (TokenStream *stream)
 {
+  gint brackets = 0;
   const gchar *end;
 
   if (stream->this != NULL)
     return;
 
-  while (g_ascii_isspace (*stream->stream))
+  while (stream->stream != stream->end && g_ascii_isspace (*stream->stream))
     stream->stream++;
 
-  switch (stream->stream[0])
+  if (stream->stream == stream->end || *stream->stream == '\0')
     {
-    case '\0':
+      stream->this = stream->stream;
       return;
+    }
 
+  switch (stream->stream[0])
+    {
     case '-': case '+': case '.': case '0': case '1': case '2':
     case '3': case '4': case '5': case '6': case '7': case '8':
     case '9':
-      for (end = stream->stream; g_ascii_isalnum (*end) ||
-           *end == '-' || *end == '+' || *end == '.'; end++);
+      for (end = stream->stream; end != stream->end; end++)
+        if (!g_ascii_isalnum (*end) &&
+            *end != '-' && *end != '+' && *end != '.')
+          break;
       break;
 
     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
@@ -57,27 +80,35 @@ token_stream_prepare (TokenStream *stream)
     case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
     case 's': case 't': case 'u': case 'v': case 'w': case 'x':
     case 'y': case 'z':
-      for (end = stream->stream; g_ascii_isalnum (*end); end++);
+      for (end = stream->stream; end != stream->end; end++)
+        if (!g_ascii_isalnum (*end))
+          break;
       break;
 
-    case '@':
+    case '@': case '%':
+      /* stop at the first space or unmatched bracket.
+       * deals nicely with cases like (%i, %i).
+       */
       for (end = stream->stream + 1;
-           *end && strchr ("abdgimnoqrstuvxy(){}*?", *end); end++);
-      break;
+           end != stream->end && !g_ascii_isspace (*end);
+           end++)
+
+        if (*end == '(' || *end == '{')
+          brackets++;
+
+        else if ((*end == ')' || *end == '}') && !brackets--)
+          break;
 
-    case '%':
-      end = stream->stream + 1;
-      g_variant_format_string_scan (&end);
       break;
 
     case '\'': case '"':
-      for (end = stream->stream + 1;
-           *end && *end != stream->stream[0] && (*end != '\\' || *++end);
-           end++);
+      for (end = stream->stream + 1; end != stream->end; end++)
+        if (*end == stream->stream[0] || *end == '\0' ||
+            (*end == '\\' && (++end == stream->end || *end == '\0')))
+          break;
 
-      if (*end)
+      if (end != stream->end && *end)
         end++;
-
       break;
 
     default:
@@ -85,14 +116,13 @@ token_stream_prepare (TokenStream *stream)
       break;
     }
 
-  stream->this = g_strndup (stream->stream, end - stream->stream);
+  stream->this = stream->stream;
   stream->stream = end;
 }
 
 static void
 token_stream_next (TokenStream *stream)
 {
-  g_free (stream->this);
   stream->this = NULL;
 }
 
@@ -102,7 +132,7 @@ token_stream_peek (TokenStream *stream,
 {
   token_stream_prepare (stream);
 
-  return stream->this && stream->this[0] == first_char;
+  return stream->this[0] == first_char;
 }
 
 static gboolean
@@ -110,8 +140,7 @@ token_stream_is_keyword (TokenStream *stream)
 {
   token_stream_prepare (stream);
 
-  return stream->this &&
-         g_ascii_isalpha (stream->this[0]);
+  return g_ascii_isalpha (stream->this[0]);
 }
 
 static gboolean
@@ -119,8 +148,7 @@ token_stream_is_numeric (TokenStream *stream)
 {
   token_stream_prepare (stream);
 
-  return stream->this &&
-         (g_ascii_isdigit (stream->this[0]) ||
+  return (g_ascii_isdigit (stream->this[0]) ||
           stream->this[0] == '-' ||
           stream->this[0] == '+' ||
           stream->this[0] == '.');
@@ -130,9 +158,12 @@ static gboolean
 token_stream_consume (TokenStream *stream,
                       const gchar *token)
 {
+  gint length = strlen (token);
+
   token_stream_prepare (stream);
 
-  if (g_strcmp0 (stream->this, token) == 0)
+  if (stream->stream - stream->this == length &&
+      memcmp (stream->this, token, length) == 0)
     {
       token_stream_next (stream);
       return TRUE;
@@ -141,6 +172,26 @@ token_stream_consume (TokenStream *stream,
   return FALSE;
 }
 
+static gboolean
+token_stream_require (TokenStream *stream,
+                      const gchar *token,
+                      const gchar *purpose)
+{
+
+  if (!token_stream_consume (stream, token))
+    {
+      gchar *message;
+
+      message = g_strdup_printf ("expected `%s'%s", token, purpose);
+      set_error (&stream->error, stream->this, stream->this, message);
+      g_free (message);
+
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
 static void
 token_stream_assert (TokenStream *stream,
                      const gchar *token)
@@ -158,8 +209,7 @@ token_stream_get (TokenStream *stream)
 
   token_stream_prepare (stream);
 
-  result = stream->this;
-  stream->this = NULL;
+  result = g_strndup (stream->this, stream->stream - stream->this);
 
   return result;
 }
@@ -319,10 +369,10 @@ pattern_from_constraints (Constraints constraints)
 
 typedef struct _AST AST;
 typedef Pattern *  (*get_pattern_func)  (AST                 *ast,
-                                         GError             **error);
+                                         GVariantParseError  *error);
 typedef GVariant * (*get_value_func)    (AST                 *ast,
                                          const GVariantType  *type,
-                                         GError             **error);
+                                         GVariantParseError  *error);
 typedef GSList *   (*get_examples_func) (AST                 *ast,
                                          gint                 index,
                                          Constraints         *constraints);
@@ -339,11 +389,14 @@ typedef struct
 struct _AST
 {
   const ASTClass *class;
+
+  const gchar *start;
+  const gchar *end;
 };
 
 static Pattern *
-ast_get_pattern (AST     *ast,
-                 GError **error)
+ast_get_pattern (AST                *ast,
+                 GVariantParseError *error)
 {
   return ast->class->get_pattern (ast, error);
 }
@@ -364,9 +417,9 @@ ast_get_pattern_size (AST *ast)
 }
 
 static GVariant *
-ast_get_value (AST                 *ast,
-               const GVariantType  *type,
-               GError             **error)
+ast_get_value (AST                *ast,
+               const GVariantType *type,
+               GVariantParseError *error)
 {
   return ast->class->get_value (ast, type, error);
 }
@@ -380,8 +433,8 @@ ast_get_examples (AST         *ast,
 }
 
 static GVariant *
-ast_resolve (AST     *ast,
-             GError **error)
+ast_resolve (AST                *ast,
+             GVariantParseError *error)
 {
   Pattern *pattern;
   GVariant *value;
@@ -407,12 +460,11 @@ ast_resolve (AST     *ast,
             examples = ast_get_examples (ast, i, &constraints);
             g_assert_cmpint (g_slist_length (examples), ==, 2);
 
-            g_set_error (error, G_VARIANT_PARSE_ERROR,
-                         G_VARIANT_PARSE_ERROR_TYPE_MISMATCH,
-                         "Unable to infer a type to contain both "
-                         "'%s' and '%s'",
-                         (gchar *) examples->data,
-                         (gchar *) examples->next->data);
+            set_error (error,
+                       "Unable to infer a type to contain both "
+                       "'%s' and '%s'",
+                       (gchar *) examples->data,
+                       (gchar *) examples->next->data);
 
             g_slist_free (examples);
             pattern_free (pattern);
@@ -436,9 +488,8 @@ ast_free (AST *ast)
   ast->class->free (ast);
 }
 
-static AST *parse (TokenStream  *stream,
-                   va_list      *app,
-                   GError      **error);
+static AST *parse (TokenStream *stream,
+                   va_list     *app);
 
 static void
 ast_array_append (AST  ***array,
@@ -462,6 +513,22 @@ ast_array_free (AST  **array,
   g_free (array);
 }
 
+static void
+ast_set_error (AST                *ast,
+               GVariantParseError *error,
+               const gchar        *format,
+               ...)
+{
+  gchar *message;
+  va_list ap;
+
+  va_start (ap, format);
+  message = g_strdup_vprintf (format, ap);
+  va_end (ap);
+
+  set_error (error, ast->start, ast->end, message);
+}
+
 typedef struct
 {
   AST ast;
@@ -471,8 +538,8 @@ typedef struct
 } Array;
 
 static Pattern *
-array_get_pattern (AST     *ast,
-                   GError **error)
+array_get_pattern (AST                *ast,
+                   GVariantParseError *error)
 {
   Array *array = (Array *) ast;
   Pattern *result = NULL;
@@ -481,9 +548,7 @@ array_get_pattern (AST     *ast,
 
   if (array->n_children == 0)
     {
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_EMPTY_ARRAY,
-                   "unable to infer the type of an empty array");
+      ast_set_error (ast, error, "unable to infer type of empty array");
       return NULL;
     }
 
@@ -503,11 +568,11 @@ array_get_pattern (AST     *ast,
 
       if (!pattern_coalesce (child_type, other_child_type))
         {
-          g_set_error (error, G_VARIANT_PARSE_ERROR,
-                       G_VARIANT_PARSE_ERROR_TYPE_MISMATCH,
-                       "unable to coalesce types '%s' and '%s'",
-                       child_type->type_string,
-                       other_child_type->type_string);
+          ast_set_error (ast, error,
+                         "unable to coalesce types '%s' and '%s'",
+                         child_type->type_string,
+                         other_child_type->type_string);
+
           pattern_free (other_child_type);
 
           break;
@@ -531,9 +596,9 @@ array_get_pattern (AST     *ast,
 }
 
 static GVariant *
-array_get_value (AST                 *ast,
-                 const GVariantType  *type,
-                 GError             **error)
+array_get_value (AST                *ast,
+                 const GVariantType *type,
+                 GVariantParseError *error)
 {
   Array *array = (Array *) ast;
   const GVariantType *childtype;
@@ -594,9 +659,8 @@ array_free (AST *ast)
 }
 
 static AST *
-array_parse (TokenStream  *stream,
-             va_list      *app,
-             GError      **error)
+array_parse (TokenStream *stream,
+             va_list     *app)
 {
   static const ASTClass array_class = {
     array_get_pattern,
@@ -617,15 +681,12 @@ array_parse (TokenStream  *stream,
     {
       AST *child;
 
-      if (need_comma && !token_stream_consume (stream, ","))
-        {
-          g_set_error (error, G_VARIANT_PARSE_ERROR,
-                       G_VARIANT_PARSE_ERROR_SYNTAX,
-                       "expecting ',' or ')' to follow tuple element");
-          goto error;
-        }
+      if (need_comma &&
+          !token_stream_require (stream, ",",
+                                 " or ']' to follow array element"))
+        goto error;
 
-      child = parse (stream, app, error);
+      child = parse (stream, app);
 
       if (!child)
         goto error;
@@ -652,8 +713,8 @@ typedef struct
 } Tuple;
 
 static Pattern *
-tuple_get_pattern (AST    *ast,
-                   GError **error)
+tuple_get_pattern (AST                *ast,
+                   GVariantParseError *error)
 {
   Tuple *tuple = (Tuple *) ast;
   Pattern *result = NULL;
@@ -692,9 +753,9 @@ tuple_get_pattern (AST    *ast,
 }
 
 static GVariant *
-tuple_get_value (AST                 *ast,
-                 const GVariantType  *type,
-                 GError             **error)
+tuple_get_value (AST                *ast,
+                 const GVariantType *type,
+                 GVariantParseError *error)
 {
   Tuple *tuple = (Tuple *) ast;
   const GVariantType *childtype;
@@ -759,9 +820,8 @@ tuple_free (AST *ast)
 }
 
 static AST *
-tuple_parse (TokenStream  *stream,
-             va_list      *app,
-             GError      **error)
+tuple_parse (TokenStream *stream,
+             va_list     *app)
 {
   static const ASTClass tuple_class = {
     tuple_get_pattern,
@@ -782,15 +842,12 @@ tuple_parse (TokenStream  *stream,
     {
       AST *child;
 
-      if (need_comma && !token_stream_consume (stream, ","))
-        {
-          g_set_error (error, G_VARIANT_PARSE_ERROR,
-                       G_VARIANT_PARSE_ERROR_SYNTAX,
-                       "expecting ',' or ')' to follow tuple element");
-          goto error;
-        }
+      if (need_comma &&
+          !token_stream_require (stream, ",",
+                                 " or ')' to follow tuple element"))
+        goto error;
 
-      child = parse (stream, app, error);
+      child = parse (stream, app);
 
       if (!child)
         goto error;
@@ -816,16 +873,16 @@ typedef struct
 } Variant;
 
 static Pattern *
-variant_get_pattern (AST     *ast,
-                     GError **error)
+variant_get_pattern (AST                *ast,
+                     GVariantParseError *error)
 {
   return pattern_from_class (G_VARIANT_TYPE_CLASS_VARIANT);
 }
 
 static GVariant *
-variant_get_value (AST                 *ast,
-                   const GVariantType  *type,
-                   GError             **error)
+variant_get_value (AST                *ast,
+                   const GVariantType *type,
+                   GVariantParseError *error)
 {
   Variant *variant = (Variant *) ast;
 
@@ -851,9 +908,8 @@ variant_free (AST *ast)
 }
 
 static AST *
-variant_parse (TokenStream  *stream,
-               va_list      *app,
-               GError      **error)
+variant_parse (TokenStream *stream,
+               va_list     *app)
 {
   static const ASTClass variant_class = {
     variant_get_pattern,
@@ -865,16 +921,13 @@ variant_parse (TokenStream  *stream,
   AST *value;
 
   token_stream_assert (stream, "<");
-  value = parse (stream, app, error);
+  value = parse (stream, app);
 
   if (!value)
     return NULL;
 
-  if (!token_stream_consume (stream, ">"))
+  if (!token_stream_require (stream, ">", " to follow variant value"))
     {
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_SYNTAX,
-                   "expecting '>' to follow variant value");
       ast_free (value);
       return NULL;
     }
@@ -896,8 +949,8 @@ typedef struct
 } Dictionary;
 
 static Pattern *
-dictionary_get_pattern (AST     *ast,
-                        GError **error)
+dictionary_get_pattern (AST                *ast,
+                        GVariantParseError *error)
 {
   Dictionary *dict = (Dictionary *) ast;
   Pattern *result = NULL;
@@ -907,9 +960,8 @@ dictionary_get_pattern (AST     *ast,
 
   if (dict->n_children == 0)
     {
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_EMPTY_ARRAY,
-                   "unable to infer the type of an empty dictionary");
+      ast_set_error (ast, error,
+                     "unable to infer the type of an empty dictionary");
       return NULL;
     }
 
@@ -938,19 +990,18 @@ dictionary_get_pattern (AST     *ast,
 
       if (!strchr ("bynqiuxtdsog?", other_pattern->type_string[0]))
         {
-          g_set_error (error, G_VARIANT_PARSE_ERROR,
-                       G_VARIANT_PARSE_ERROR_EMPTY_ARRAY,
-                       "dictionary keys must have basic types");
+          ast_set_error (ast, error, "dictionary keys must have basic types");
           pattern_free (other_pattern);
           break;
         }
 
       if (!pattern_coalesce (key_pattern, other_pattern))
         {
-          g_set_error (error, G_VARIANT_PARSE_ERROR,
-                       G_VARIANT_PARSE_ERROR_TYPE_MISMATCH,
-                       "unable to coalesce types '%s' and '%s'",
-                       key_pattern->type_string, other_pattern->type_string);
+          ast_set_error (ast, error,
+                         "unable to coalesce types '%s' and '%s'",
+                         key_pattern->type_string,
+                         other_pattern->type_string);
+
           pattern_free (other_pattern);
 
           break;
@@ -966,10 +1017,11 @@ dictionary_get_pattern (AST     *ast,
 
       if (!pattern_coalesce (value_pattern, other_pattern))
         {
-          g_set_error (error, G_VARIANT_PARSE_ERROR,
-                       G_VARIANT_PARSE_ERROR_TYPE_MISMATCH,
-                       "unable to coalesce types '%s' and '%s'",
-                       key_pattern->type_string, other_pattern->type_string);
+          ast_set_error (ast, error,
+                         "unable to coalesce types '%s' and '%s'",
+                         key_pattern->type_string,
+                         other_pattern->type_string);
+
           pattern_free (other_pattern);
 
           break;
@@ -999,9 +1051,9 @@ dictionary_get_pattern (AST     *ast,
 }
 
 static GVariant *
-dictionary_get_value (AST                 *ast,
-                      const GVariantType  *type,
-                      GError             **error)
+dictionary_get_value (AST                *ast,
+                      const GVariantType *type,
+                      GVariantParseError *error)
 {
   Dictionary *dict = (Dictionary *) ast;
 
@@ -1129,9 +1181,8 @@ dictionary_free (AST *ast)
 }
 
 static AST *
-dictionary_parse (TokenStream  *stream,
-                  va_list      *app,
-                  GError      **error)
+dictionary_parse (TokenStream *stream,
+                  va_list     *app)
 {
   static const ASTClass dictionary_class = {
     dictionary_get_pattern,
@@ -1152,34 +1203,26 @@ dictionary_parse (TokenStream  *stream,
 
   token_stream_assert (stream, "{");
 
-  if ((first = parse (stream, app, error)) == NULL)
+  if ((first = parse (stream, app)) == NULL)
     goto error;
 
   ast_array_append (&dict->keys, &n_keys, first);
 
   only_one = token_stream_consume (stream, ",");
-  if (!only_one && !token_stream_consume (stream, ":"))
-    {
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_SYNTAX,
-                   "expecting ',' or ':' to follow dictionary entry key");
-      goto error;
-    }
+  if (!only_one &&
+      !token_stream_require (stream, ":",
+                             " or ':' to follow dictionary entry key"))
+    goto error;
 
-  if ((first = parse (stream, app, error)) == NULL)
+  if ((first = parse (stream, app)) == NULL)
     goto error;
 
   ast_array_append (&dict->values, &n_values, first);
 
   if (only_one)
     {
-      if (!token_stream_consume (stream, "}"))
-        {
-          g_set_error (error, G_VARIANT_PARSE_ERROR,
-                       G_VARIANT_PARSE_ERROR_SYNTAX,
-                       "expecting '}' at end of dictionary entry");
-          goto error;
-        }
+      if (!token_stream_require (stream, "}", " at end of dictionary entry"))
+        goto error;
 
       g_assert (n_keys == 1 && n_values == 1);
       dict->n_children = -1;
@@ -1191,30 +1234,22 @@ dictionary_parse (TokenStream  *stream,
     {
       AST *child;
 
-      if (!token_stream_consume (stream, ","))
-        {
-          g_set_error (error, G_VARIANT_PARSE_ERROR,
-                       G_VARIANT_PARSE_ERROR_SYNTAX,
-                       "expecting ',' or '}' to follow dictionary entry");
-          goto error;
-        }
+      if (!token_stream_require (stream, ",",
+                                 " or '}' to follow dictionary entry"))
+        goto error;
 
-      child = parse (stream, app, error);
+      child = parse (stream, app);
 
       if (!child)
         goto error;
         
       ast_array_append (&dict->keys, &n_keys, child);
 
-      if (!token_stream_consume (stream, ":"))
-        {
-          g_set_error (error, G_VARIANT_PARSE_ERROR,
-                       G_VARIANT_PARSE_ERROR_SYNTAX,
-                       "expecting ':' to follow dictionary entry key");
-          goto error;
-        }
+      if (!token_stream_require (stream, ":",
+                                 " to follow dictionary entry key"))
+        goto error;
 
-      child = parse (stream, app, error);
+      child = parse (stream, app);
 
       if (!child)
         goto error;
@@ -1239,8 +1274,8 @@ typedef struct
 {
   AST ast;
 
-  gchar *token;
   gchar *string;
+  gchar *token;
 } Terminal;
 
 static Constraints
@@ -1256,8 +1291,8 @@ constraints_from_string (const gchar *token)
 }
 
 static Pattern *
-terminal_get_pattern (AST     *ast,
-                      GError **error)
+terminal_get_pattern (AST                *ast,
+                      GVariantParseError *error)
 {
   Terminal *terminal = (Terminal *) ast;
   Constraints constraints;
@@ -1268,9 +1303,10 @@ terminal_get_pattern (AST     *ast,
 }
 
 static gboolean
-parse_string (const gchar  *token,
-              gchar       **result,
-              GError      **error)
+parse_string (const gchar         *token,
+              gchar              **result,
+              const gchar         *start,
+              GVariantParseError  *error)
 {
   gint length;
   gchar *tmp;
@@ -1279,9 +1315,8 @@ parse_string (const gchar  *token,
 
   if (length < 2 || token[0] != token[length - 1])
     {
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_SYNTAX,
-                   "Unterminated string constant");
+      set_error (error, start, start + strlen (token),
+                 "unterminated string constant");
       return FALSE;
     }
 
@@ -1293,11 +1328,12 @@ parse_string (const gchar  *token,
 }
 
 static gboolean
-parse_signed (const gchar  *token,
-              gint64        min,
-              gint64        max,
-              gint64       *result,
-              GError      **error)
+parse_signed (const gchar        *token,
+              gint64              min,
+              gint64              max,
+              gint64             *result,
+              AST                *ast,
+              GVariantParseError *error)
 {
   gint64 value;
   gchar *end;
@@ -1307,17 +1343,15 @@ parse_signed (const gchar  *token,
 
   if (errno == ERANGE || value < min || value > max)
     {
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_SYNTAX,
-                   "Signed integer '%s' is too large for type", token);
+      ast_set_error (ast, error,
+                     "Signed integer '%s' is too large for type", token);
       return FALSE;
     }
 
   if (*end)
     {
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_SYNTAX,
-                   "Unable to parse signed integer '%s'\n", token);
+      ast_set_error (ast, error,
+                     "Unable to parse signed integer '%s'", token);
       return FALSE;
     }
 
@@ -1327,10 +1361,11 @@ parse_signed (const gchar  *token,
 }
 
 static gboolean
-parse_unsigned (const gchar  *token,
-                guint64       max,
-                guint64      *result,
-                GError      **error)
+parse_unsigned (const gchar        *token,
+                guint64             max,
+                guint64            *result,
+                AST                *ast,
+                GVariantParseError *error)
 {
   guint64 value;
   gchar *end;
@@ -1344,18 +1379,16 @@ parse_unsigned (const gchar  *token,
 
   if (errno == ERANGE || value > max)
     {
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_SYNTAX,
-                   "Unsigned integer '%s' is too large for type", token);
+      ast_set_error (ast, error,
+                     "Unsigned integer '%s' is too large for type", token);
       return FALSE;
     }
 
   if (*end)
     {
  error:
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_SYNTAX,
-                   "Unable to parse unsigned integer '%s'\n", token);
+      ast_set_error (ast, error,
+                     "Unable to parse unsigned integer '%s'", token);
       return FALSE;
     }
 
@@ -1365,9 +1398,10 @@ parse_unsigned (const gchar  *token,
 }
 
 static gboolean
-parse_floating (const gchar  *token,
-                gdouble      *result,
-                GError      **error)
+parse_floating (const gchar        *token,
+                gdouble            *result,
+                AST                *ast,
+                GVariantParseError *error)
 {
   gdouble value;
   gchar *end;
@@ -1377,17 +1411,15 @@ parse_floating (const gchar  *token,
 
   if (errno == ERANGE)
     {
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_SYNTAX,
-                   "Floating point value '%s' is too large", token);
+      ast_set_error (ast, error,
+                     "Floating point value '%s' is too large", token);
       return FALSE;
     }
 
   if (*end)
     {
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_SYNTAX,
-                   "Unable to parse floating point value '%s'\n", token);
+      ast_set_error (ast, error,
+                     "Unable to parse floating point value '%s'", token);
       return FALSE;
     }
 
@@ -1397,9 +1429,9 @@ parse_floating (const gchar  *token,
 }
 
 static GVariant *
-terminal_get_value (AST                 *ast,
-                    const GVariantType  *type,
-                    GError             **error)
+terminal_get_value (AST                *ast,
+                    const GVariantType *type,
+                    GVariantParseError *error)
 {
   Terminal *terminal = (Terminal *) ast;
 
@@ -1407,41 +1439,28 @@ terminal_get_value (AST                 *ast,
     {
     case G_VARIANT_TYPE_CLASS_BOOLEAN:
       {
-        g_set_error (error, G_VARIANT_PARSE_ERROR,
-                     G_VARIANT_PARSE_ERROR_SYNTAX,
-                     "Unable to parse as boolean: %s", terminal->token);
+        ast_set_error (ast, error,
+                       "Unable to parse as boolean: %s", terminal->token);
         return NULL;
       }
 
     case G_VARIANT_TYPE_CLASS_BYTE:
-      if (terminal->token[0] == '\'' || terminal->token[0] == '"')
+      if (terminal->string != NULL)
         {
-          gchar *string;
-          gchar value;
-
-          if (!parse_string (terminal->token, &string, error))
-            return NULL;
-
-          if (strlen (string) != 1)
+          if (strlen (terminal->string) != 1)
             {
-              g_set_error (error, G_VARIANT_PARSE_ERROR,
-                           G_VARIANT_PARSE_ERROR_SYNTAX,
-                           "Character constants must be one character");
-              g_free (string);
-
+              ast_set_error (ast, error,
+                             "Character constants must be one character");
               return NULL;
             }
 
-          value = string[0];
-          g_free (string);
-
-          return g_variant_new_byte (value);
+          return g_variant_new_byte (terminal->string[0]);
         }
       else
         {
           guint64 value;
 
-          if (!parse_unsigned (terminal->token, 255, &value, error))
+          if (!parse_unsigned (terminal->token, 255, &value, ast, error))
             return NULL;
 
           return g_variant_new_byte (value);
@@ -1453,7 +1472,7 @@ terminal_get_value (AST                 *ast,
 
         if (!parse_signed (terminal->token,
                            G_MININT16, G_MAXINT16,
-                           &value, error))
+                           &value, ast, error))
           return NULL;
 
         return g_variant_new_int16 (value);
@@ -1465,7 +1484,7 @@ terminal_get_value (AST                 *ast,
 
         if (!parse_unsigned (terminal->token,
                              G_MAXUINT16,
-                             &value, error))
+                             &value, ast, error))
           return NULL;
 
         return g_variant_new_uint16 (value);
@@ -1477,7 +1496,7 @@ terminal_get_value (AST                 *ast,
 
         if (!parse_signed (terminal->token,
                            G_MININT32, G_MAXINT32,
-                           &value, error))
+                           &value, ast, error))
           return NULL;
 
         return g_variant_new_int32 (value);
@@ -1489,7 +1508,7 @@ terminal_get_value (AST                 *ast,
 
         if (!parse_unsigned (terminal->token,
                              G_MAXUINT32,
-                             &value, error))
+                             &value, ast, error))
           return NULL;
 
         return g_variant_new_uint32 (value);
@@ -1501,7 +1520,7 @@ terminal_get_value (AST                 *ast,
 
         if (!parse_signed (terminal->token,
                            G_MININT64, G_MAXINT64,
-                           &value, error))
+                           &value, ast, error))
           return NULL;
 
         return g_variant_new_int64 (value);
@@ -1513,7 +1532,7 @@ terminal_get_value (AST                 *ast,
 
         if (!parse_unsigned (terminal->token,
                              G_MAXUINT64,
-                             &value, error))
+                             &value, ast, error))
           return NULL;
 
         return g_variant_new_uint64 (value);
@@ -1523,7 +1542,7 @@ terminal_get_value (AST                 *ast,
       {
         gdouble floating;
 
-        if (!parse_floating (terminal->token, &floating, error))
+        if (!parse_floating (terminal->token, &floating, ast, error))
           return NULL;
 
         return g_variant_new_double (floating);
@@ -1531,20 +1550,13 @@ terminal_get_value (AST                 *ast,
 
     case G_VARIANT_TYPE_CLASS_STRING:
       {
-        GVariant *value;
-
         if (terminal->string == NULL)
           {
-            g_set_error (error, G_VARIANT_PARSE_ERROR,
-                         G_VARIANT_PARSE_ERROR_SYNTAX,
-                         "Unable to parse as a string: %s",
-                         terminal->token);
+            ast_set_error (ast, error, "unable to parse as string");
             return NULL;
           }
 
-        value = g_variant_new_string (terminal->string);
-
-        return value;
+        return g_variant_new_string (terminal->string);
       }
 
     default:
@@ -1578,14 +1590,12 @@ terminal_free (AST *ast)
   Terminal *terminal = (Terminal *) ast;
 
   g_free (terminal->token);
-  g_free (terminal->string);
   g_slice_free (Terminal, terminal);
 }
 
 static AST *
-terminal_parse (TokenStream  *stream,
-                va_list      *app,
-                GError      **error)
+terminal_parse (TokenStream *stream,
+                va_list     *app)
 {
   static const ASTClass terminal_class = {
     terminal_get_pattern,
@@ -1599,17 +1609,9 @@ terminal_parse (TokenStream  *stream,
 
   token = token_stream_get (stream);
 
-  if (token == NULL)
-    {
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_SYNTAX,
-                   "expecting token");
-      return NULL;
-    }
-
   if (token[0] == '"' || token[0] == '\'')
     {
-      if (!parse_string (token, &string, error))
+      if (!parse_string (token, &string, stream->this, &stream->error))
         {
           g_free (token);
           return NULL;
@@ -1623,6 +1625,8 @@ terminal_parse (TokenStream  *stream,
   terminal->token = token;
   terminal->string = string;
 
+  token_stream_next (stream);
+
   return (AST *) terminal;
 }
 
@@ -1633,8 +1637,8 @@ typedef struct
 } Boolean;
 
 static Pattern *
-boolean_get_pattern (AST     *ast,
-                     GError **error)
+boolean_get_pattern (AST                *ast,
+                     GVariantParseError *error)
 {
   /* do this as a constraint so that
    * we get more consistent errors
@@ -1643,9 +1647,9 @@ boolean_get_pattern (AST     *ast,
 }
 
 static GVariant *
-boolean_get_value (AST                 *ast,
-                   const GVariantType  *type,
-                   GError             **error)
+boolean_get_value (AST                *ast,
+                   const GVariantType *type,
+                   GVariantParseError *error)
 {
   Boolean *boolean = (Boolean *) ast;
 
@@ -1708,8 +1712,8 @@ typedef struct
 } Positional;
 
 static Pattern *
-positional_get_pattern (AST     *ast,
-                        GError **error)
+positional_get_pattern (AST                *ast,
+                        GVariantParseError *error)
 {
   Positional *positional = (Positional *) ast;
 
@@ -1717,9 +1721,9 @@ positional_get_pattern (AST     *ast,
 }
 
 static GVariant *
-positional_get_value (AST                 *ast,
-                      const GVariantType  *type,
-                      GError             **error)
+positional_get_value (AST                *ast,
+                      const GVariantType *type,
+                      GVariantParseError *error)
 {
   Positional *positional = (Positional *) ast;
   GVariant *tmp;
@@ -1755,9 +1759,8 @@ positional_free (AST *ast)
 }
 
 static AST *
-positional_parse (TokenStream  *stream,
-                  va_list      *app,
-                  GError      **error)
+positional_parse (TokenStream *stream,
+                  va_list     *app)
 {
   static const ASTClass positional_class = {
     positional_get_pattern,
@@ -1780,9 +1783,8 @@ positional_parse (TokenStream  *stream,
 
   if (*format)
     {
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_SYNTAX,
-                   "invalid GVariant format string: %s", token + 1);
+      set_error (&stream->error, stream->stream, stream->stream,
+                 "invalid GVariant format string");
 
       g_variant_unref (g_variant_ref_sink (positional->value));
       g_slice_free (Positional, positional);
@@ -1791,6 +1793,7 @@ positional_parse (TokenStream  *stream,
       return NULL;
     }
 
+  token_stream_next (stream);
   g_free (token);
 
   return (AST *) positional;
@@ -1805,8 +1808,8 @@ typedef struct
 } TypeDecl;
 
 static Pattern *
-typedecl_get_pattern (AST     *ast,
-                      GError **error)
+typedecl_get_pattern (AST                *ast,
+                      GVariantParseError *error)
 {
   TypeDecl *decl = (TypeDecl *) ast;
 
@@ -1814,9 +1817,9 @@ typedecl_get_pattern (AST     *ast,
 }
 
 static GVariant *
-typedecl_get_value (AST                 *ast,
-                    const GVariantType  *type,
-                    GError             **error)
+typedecl_get_value (AST                *ast,
+                    const GVariantType *type,
+                    GVariantParseError *error)
 {
   TypeDecl *decl = (TypeDecl *) ast;
 
@@ -1842,9 +1845,8 @@ typedecl_free (AST *ast)
 }
 
 static AST *
-typedecl_parse (TokenStream  *stream,
-                va_list      *app,
-                GError      **error)
+typedecl_parse (TokenStream *stream,
+                va_list     *app)
 {
   static const ASTClass typedecl_class = {
     typedecl_get_pattern,
@@ -1864,9 +1866,8 @@ typedecl_parse (TokenStream  *stream,
 
       if (!g_variant_type_string_is_valid (token + 1))
         {
-          g_set_error (error, G_VARIANT_PARSE_ERROR,
-                       G_VARIANT_PARSE_ERROR_SYNTAX,
-                       "Invalid type declaration '%s'", token);
+          set_error (&stream->error, stream->stream, stream->stream,
+                     "Invalid type declaration");
           g_free (token);
 
           return NULL;
@@ -1876,15 +1877,15 @@ typedecl_parse (TokenStream  *stream,
 
       if (!g_variant_type_is_concrete (type))
         {
-          g_set_error (error, G_VARIANT_PARSE_ERROR,
-                       G_VARIANT_PARSE_ERROR_SYNTAX,
-                       "Type declarations must be concrete: '%s'", token);
+          set_error (&stream->error, stream->stream, stream->stream,
+                     "Type declarations must be concrete");
           g_variant_type_free (type);
           g_free (token);
 
           return NULL;
         }
 
+      token_stream_next (stream);
       g_free (token);
     }
   else
@@ -1927,19 +1928,14 @@ typedecl_parse (TokenStream  *stream,
 
       else
         {
-          gchar *token;
-
-          token = token_stream_get (stream);
-          g_set_error (error, G_VARIANT_PARSE_ERROR,
-                       G_VARIANT_PARSE_ERROR_SYNTAX,
-                       "unknown keyword: %s", token);
-          g_free (token);
+          set_error (&stream->error, stream->stream, stream->stream,
+                     "unknown keyword");
 
           return NULL;
         }
     }
 
-  if ((child = parse (stream, app, error)) == NULL)
+  if ((child = parse (stream, app)) == NULL)
     {
       g_variant_type_free (type);
       return NULL;
@@ -1954,87 +1950,139 @@ typedecl_parse (TokenStream  *stream,
 }
 
 static AST *
-parse (TokenStream  *stream,
-       va_list      *app,
-       GError      **error)
+parse (TokenStream *stream,
+       va_list     *app)
 {
+  const gchar *start;
+  AST *result;
+
+  token_stream_prepare (stream);
+  start = stream->this;
+
   if (token_stream_peek (stream, '['))
-    return array_parse (stream, app, error);
+    result = array_parse (stream, app);
 
-  if (token_stream_peek (stream, '('))
-    return tuple_parse (stream, app, error);
+  else if (token_stream_peek (stream, '('))
+    result = tuple_parse (stream, app);
 
-  if (token_stream_peek (stream, '<'))
-    return variant_parse (stream, app, error);
+  else if (token_stream_peek (stream, '<'))
+    result = variant_parse (stream, app);
 
-  if (token_stream_peek (stream, '{'))
-    return dictionary_parse (stream, app, error);
+  else if (token_stream_peek (stream, '{'))
+    result = dictionary_parse (stream, app);
 
-  if (app && token_stream_peek (stream, '%'))
-    return positional_parse (stream, app, error);
+  else if (app && token_stream_peek (stream, '%'))
+    result = positional_parse (stream, app);
 
-  if (token_stream_consume (stream, "true"))
-    return boolean_new (TRUE);
+  else if (token_stream_consume (stream, "true"))
+    result = boolean_new (TRUE);
 
-  if (token_stream_consume (stream, "false"))
-    return boolean_new (FALSE);
+  else if (token_stream_consume (stream, "false"))
+    result = boolean_new (FALSE);
 
-  if (token_stream_peek (stream, '@') || token_stream_is_keyword (stream))
-    return typedecl_parse (stream, app, error);
+  else if (token_stream_peek (stream, '@') || token_stream_is_keyword (stream))
+    result = typedecl_parse (stream, app);
 
-  if (token_stream_is_numeric (stream) ||
-      token_stream_peek (stream, '\'') ||
-      token_stream_peek (stream, '"'))
-    return terminal_parse (stream, app, error);
+  else if (token_stream_is_numeric (stream) ||
+           token_stream_peek (stream, '\'') ||
+           token_stream_peek (stream, '"'))
+    result = terminal_parse (stream, app);
 
-  {
-    gchar *token;
+  else
+    {
+      gchar *token = token_stream_get (stream);
 
-    token = token_stream_get (stream);
+      if (token[0])
+        {
+          gchar *escaped = g_strescape (token, 0);
+          set_error (&stream->error, stream->this, stream->stream,
+                     "stray symbol");
+          g_free (escaped);
+        }
+      else
+        {
+          set_error (&stream->error, stream->stream, stream->stream,
+                     "expected value");
+        }
 
-    if (token)
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_SYNTAX,
-                   "Don't know what to do with token: %s\n", token);
+      g_free (token);
 
-    else
-      g_set_error (error, G_VARIANT_PARSE_ERROR,
-                   G_VARIANT_PARSE_ERROR_SYNTAX,
-                   "Unexpected end of text");
+      return NULL;
+    }
 
-    g_free (token);
+  if (result != NULL)
+    {
+      result->start = start;
+      result->end = stream->stream;
+    }
 
-    return NULL;
-  }
+  return result;
+}
+
+GVariant *
+g_variant_parse_full (const gchar        **text,
+                      const gchar         *end,
+                      const GVariantType  *type,
+                      GVariantParseError  *error)
+{
+  TokenStream stream = {  };
+  GVariant *result = NULL;
+  AST *ast;
+
+  g_return_val_if_fail (text != NULL, NULL);
+  g_return_val_if_fail (*text == end || *text != NULL, NULL);
+
+  stream.stream = *text;
+  stream.end = end;
+
+  if ((ast = parse (&stream, NULL)))
+    {
+      result = ast_resolve (ast, &stream.error);
+
+      if (result != NULL)
+        *text = stream.stream;
+
+      ast_free (ast);
+    }
+
+  if (error != NULL)
+    *error = stream.error;
+
+  return result;
 }
 
 GVariant *
 g_variant_parsef_va (const gchar *format,
                      va_list     *app)
 {
-  TokenStream stream = { format };
-  GError *error = NULL;
-  GVariant *result;
+  TokenStream stream = {  };
+  GVariant *result = NULL;
   AST *ast;
 
   g_return_val_if_fail (format != NULL, NULL);
   g_return_val_if_fail (app != NULL, NULL);
 
-  if ((ast = parse (&stream, app, &error)) == NULL)
-    {
-      g_critical ("g_variant_parsef: %s", error->message);
-      g_error_free (error);
+  stream.stream = format;
+  stream.end = NULL;
 
-      return NULL;
+  if ((ast = parse (&stream, app)))
+    {
+      result = ast_resolve (ast, &stream.error);
+      ast_free (ast);
     }
 
-  result = ast_resolve (ast, &error);
-  ast_free (ast);
+  if (result != NULL)
+    {
+      while (g_ascii_isspace (*stream.stream))
+        stream.stream++;
 
-  if (result == NULL)
+      if (*stream.stream)
+        g_critical ("g_variant_parsef: trailing text after value");
+    }
+  else
     {
-      g_critical ("g_variant_parsef: %s", error->message);
-      g_error_free (error);
+      g_critical ("g_variant_parsef: %s", stream.error.error);
+      g_free (stream.error.error);
     }
 
   return result;
@@ -2060,29 +2108,31 @@ g_variant_parse (const gchar         *text,
                  const GVariantType  *type,
                  GError             **error)
 {
-  TokenStream stream = { text };
+  GVariantParseError parse_error;
   GVariant *result;
-  gchar *tmp;
-  AST *ast;
+  const gchar *end;
 
   g_return_val_if_fail (text != NULL || text_length == 0, NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  if (text_length != -1)
-    {
-      tmp = g_strndup (text, text_length);
-      stream.stream = tmp;
-    }
+  if (text_length >= 0)
+    end = text + text_length;
   else
-    tmp = NULL;
-
-  if ((ast = parse (&stream, NULL, error)) == NULL)
-    return NULL;
+    end = NULL;
 
-  result = ast_resolve (ast, error);
-  ast_free (ast);
+  if ((result = g_variant_parse_full (&text, end, type, &parse_error)))
+    {
+      while (g_ascii_isspace (*text))
+        text++;
 
-  g_free (tmp);
+      if (*text)
+        g_set_error_literal (error, 0, 0, "trailing text after value");
+    }
+  else
+    {
+      g_set_error_literal (error, 0, 0, parse_error.error);
+      g_free (parse_error.error);
+    }
 
   return result;
 }
diff --git a/glib/gvariant.h b/glib/gvariant.h
index 0056a61..d408642 100644
--- a/glib/gvariant.h
+++ b/glib/gvariant.h
@@ -146,6 +146,13 @@ GVariant                       *g_variant_builder_end                   (GVarian
 void                            g_variant_builder_cancel                (GVariantBuilder      *builder);
 
 /* text printing/parsing */
+typedef struct
+{
+  const gchar *start;
+  const gchar *end;
+  gchar *error;
+} GVariantParseError;
+
 gchar                          *g_variant_print                         (GVariant             *value,
                                                                          gboolean              type_annotate);
 GString                        *g_variant_print_string                  (GVariant             *value,
@@ -159,6 +166,10 @@ GVariant *                      g_variant_parsef                        (const g
                                                                          ...);
 GVariant *                      g_variant_parsef_va                     (const gchar          *format,
                                                                          va_list              *app);
+GVariant *                      g_variant_parse_full                    (const gchar         **text,
+                                                                         const gchar          *end,
+                                                                         const GVariantType   *type,
+                                                                         GVariantParseError   *error);
 
 /* markup printing/parsing */
 gchar                          *g_variant_markup_print                  (GVariant             *value,
@@ -196,14 +207,4 @@ typedef enum
   G_VARIANT_BUILDER_ERROR_TYPE
 } GVariantBuilderError;
 
-#define G_VARIANT_PARSE_ERROR \
-    (g_quark_from_string ("GVariantParseError"))
-
-typedef enum
-{
-  G_VARIANT_PARSE_ERROR_EMPTY_ARRAY,
-  G_VARIANT_PARSE_ERROR_TYPE_MISMATCH,
-  G_VARIANT_PARSE_ERROR_SYNTAX,
-} GVariantParseError;
-
 #endif /* _gvariant_h_ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]