Blame glib/gvariant-parser.c

Packit ae235b
/*
Packit ae235b
 * Copyright © 2009, 2010 Codethink Limited
Packit ae235b
 *
Packit ae235b
 * This library is free software; you can redistribute it and/or
Packit ae235b
 * modify it under the terms of the GNU Lesser General Public
Packit ae235b
 * License as published by the Free Software Foundation; either
Packit ae235b
 * version 2.1 of the License, or (at your option) any later version.
Packit ae235b
 *
Packit ae235b
 * This library is distributed in the hope that it will be useful,
Packit ae235b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit ae235b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit ae235b
 * Lesser General Public License for more details.
Packit ae235b
 *
Packit ae235b
 * You should have received a copy of the GNU Lesser General Public
Packit ae235b
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit ae235b
 *
Packit ae235b
 * Author: Ryan Lortie <desrt@desrt.ca>
Packit ae235b
 */
Packit ae235b
Packit ae235b
#include "config.h"
Packit ae235b
Packit ae235b
#include <stdlib.h>
Packit ae235b
#include <string.h>
Packit ae235b
#include <errno.h>
Packit ae235b
Packit ae235b
#include "gerror.h"
Packit ae235b
#include "gquark.h"
Packit ae235b
#include "gstring.h"
Packit ae235b
#include "gstrfuncs.h"
Packit ae235b
#include "gtestutils.h"
Packit ae235b
#include "gvariant.h"
Packit ae235b
#include "gvarianttype.h"
Packit ae235b
#include "gslice.h"
Packit ae235b
#include "gthread.h"
Packit ae235b
Packit ae235b
/*
Packit ae235b
 * two-pass algorithm
Packit ae235b
 * designed by ryan lortie and william hua
Packit ae235b
 * designed in itb-229 and at ghazi's, 2009.
Packit ae235b
 */
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * G_VARIANT_PARSE_ERROR:
Packit ae235b
 *
Packit ae235b
 * Error domain for GVariant text format parsing.  Specific error codes
Packit ae235b
 * are not currently defined for this domain.  See #GError for
Packit ae235b
 * information on error domains.
Packit ae235b
 **/
Packit ae235b
/**
Packit ae235b
 * GVariantParseError:
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_FAILED: generic error (unused)
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_BASIC_TYPE_EXPECTED: a non-basic #GVariantType was given where a basic type was expected
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_CANNOT_INFER_TYPE: cannot infer the #GVariantType
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_DEFINITE_TYPE_EXPECTED: an indefinite #GVariantType was given where a definite type was expected
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_INPUT_NOT_AT_END: extra data after parsing finished
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_INVALID_CHARACTER: invalid character in number or unicode escape
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_INVALID_FORMAT_STRING: not a valid #GVariant format string
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_INVALID_OBJECT_PATH: not a valid object path
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_INVALID_SIGNATURE: not a valid type signature
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_INVALID_TYPE_STRING: not a valid #GVariant type string
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_NO_COMMON_TYPE: could not find a common type for array entries
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_NUMBER_OUT_OF_RANGE: the numerical value is out of range of the given type
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_NUMBER_TOO_BIG: the numerical value is out of range for any type
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_TYPE_ERROR: cannot parse as variant of the specified type
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_UNEXPECTED_TOKEN: an unexpected token was encountered
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_UNKNOWN_KEYWORD: an unknown keyword was encountered
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT: unterminated string constant
Packit ae235b
 * @G_VARIANT_PARSE_ERROR_VALUE_EXPECTED: no value given
Packit ae235b
 *
Packit ae235b
 * Error codes returned by parsing text-format GVariants.
Packit ae235b
 **/
Packit ae235b
G_DEFINE_QUARK (g-variant-parse-error-quark, g_variant_parse_error)
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_variant_parser_get_error_quark:
Packit ae235b
 *
Packit ae235b
 * Same as g_variant_error_quark().
Packit ae235b
 *
Packit ae235b
 * Deprecated: Use g_variant_parse_error_quark() instead.
Packit ae235b
 */
Packit ae235b
GQuark
Packit ae235b
g_variant_parser_get_error_quark (void)
Packit ae235b
{
Packit ae235b
  return g_variant_parse_error_quark ();
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  gint start, end;
Packit ae235b
} SourceRef;
Packit ae235b
Packit ae235b
G_GNUC_PRINTF(5, 0)
Packit ae235b
static void
Packit ae235b
parser_set_error_va (GError      **error,
Packit ae235b
                     SourceRef    *location,
Packit ae235b
                     SourceRef    *other,
Packit ae235b
                     gint          code,
Packit ae235b
                     const gchar  *format,
Packit ae235b
                     va_list       ap)
Packit ae235b
{
Packit ae235b
  GString *msg = g_string_new (NULL);
Packit ae235b
Packit ae235b
  if (location->start == location->end)
Packit ae235b
    g_string_append_printf (msg, "%d", location->start);
Packit ae235b
  else
Packit ae235b
    g_string_append_printf (msg, "%d-%d", location->start, location->end);
Packit ae235b
Packit ae235b
  if (other != NULL)
Packit ae235b
    {
Packit ae235b
      g_assert (other->start != other->end);
Packit ae235b
      g_string_append_printf (msg, ",%d-%d", other->start, other->end);
Packit ae235b
    }
Packit ae235b
  g_string_append_c (msg, ':');
Packit ae235b
Packit ae235b
  g_string_append_vprintf (msg, format, ap);
Packit ae235b
  g_set_error_literal (error, G_VARIANT_PARSE_ERROR, code, msg->str);
Packit ae235b
  g_string_free (msg, TRUE);
Packit ae235b
}
Packit ae235b
Packit ae235b
G_GNUC_PRINTF(5, 6)
Packit ae235b
static void
Packit ae235b
parser_set_error (GError      **error,
Packit ae235b
                  SourceRef    *location,
Packit ae235b
                  SourceRef    *other,
Packit ae235b
                  gint          code,
Packit ae235b
                  const gchar  *format,
Packit ae235b
                  ...)
Packit ae235b
{
Packit ae235b
  va_list ap;
Packit ae235b
Packit ae235b
  va_start (ap, format);
Packit ae235b
  parser_set_error_va (error, location, other, code, format, ap);
Packit ae235b
  va_end (ap);
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  const gchar *start;
Packit ae235b
  const gchar *stream;
Packit ae235b
  const gchar *end;
Packit ae235b
Packit ae235b
  const gchar *this;
Packit ae235b
} TokenStream;
Packit ae235b
Packit ae235b
Packit ae235b
G_GNUC_PRINTF(5, 6)
Packit ae235b
static void
Packit ae235b
token_stream_set_error (TokenStream  *stream,
Packit ae235b
                        GError      **error,
Packit ae235b
                        gboolean      this_token,
Packit ae235b
                        gint          code,
Packit ae235b
                        const gchar  *format,
Packit ae235b
                        ...)
Packit ae235b
{
Packit ae235b
  SourceRef ref;
Packit ae235b
  va_list ap;
Packit ae235b
Packit ae235b
  ref.start = stream->this - stream->start;
Packit ae235b
Packit ae235b
  if (this_token)
Packit ae235b
    ref.end = stream->stream - stream->start;
Packit ae235b
  else
Packit ae235b
    ref.end = ref.start;
Packit ae235b
Packit ae235b
  va_start (ap, format);
Packit ae235b
  parser_set_error_va (error, &ref, NULL, code, format, ap);
Packit ae235b
  va_end (ap);
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
token_stream_prepare (TokenStream *stream)
Packit ae235b
{
Packit ae235b
  gint brackets = 0;
Packit ae235b
  const gchar *end;
Packit ae235b
Packit ae235b
  if (stream->this != NULL)
Packit ae235b
    return TRUE;
Packit ae235b
Packit ae235b
  while (stream->stream != stream->end && g_ascii_isspace (*stream->stream))
Packit ae235b
    stream->stream++;
Packit ae235b
Packit ae235b
  if (stream->stream == stream->end || *stream->stream == '\0')
Packit ae235b
    {
Packit ae235b
      stream->this = stream->stream;
Packit ae235b
      return FALSE;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  switch (stream->stream[0])
Packit ae235b
    {
Packit ae235b
    case '-': case '+': case '.': case '0': case '1': case '2':
Packit ae235b
    case '3': case '4': case '5': case '6': case '7': case '8':
Packit ae235b
    case '9':
Packit ae235b
      for (end = stream->stream; end != stream->end; end++)
Packit ae235b
        if (!g_ascii_isalnum (*end) &&
Packit ae235b
            *end != '-' && *end != '+' && *end != '.')
Packit ae235b
          break;
Packit ae235b
      break;
Packit ae235b
Packit ae235b
    case 'b':
Packit ae235b
      if (stream->stream[1] == '\'' || stream->stream[1] == '"')
Packit ae235b
        {
Packit ae235b
          for (end = stream->stream + 2; end != stream->end; end++)
Packit ae235b
            if (*end == stream->stream[1] || *end == '\0' ||
Packit ae235b
                (*end == '\\' && (++end == stream->end || *end == '\0')))
Packit ae235b
              break;
Packit ae235b
Packit ae235b
          if (end != stream->end && *end)
Packit ae235b
            end++;
Packit ae235b
          break;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      else
Packit ae235b
        {
Packit ae235b
          /* ↓↓↓ */
Packit ae235b
        }
Packit ae235b
Packit ae235b
    case 'a': /* 'b' */ case 'c': case 'd': case 'e': case 'f':
Packit ae235b
    case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
Packit ae235b
    case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
Packit ae235b
    case 's': case 't': case 'u': case 'v': case 'w': case 'x':
Packit ae235b
    case 'y': case 'z':
Packit ae235b
      for (end = stream->stream; end != stream->end; end++)
Packit ae235b
        if (!g_ascii_isalnum (*end))
Packit ae235b
          break;
Packit ae235b
      break;
Packit ae235b
Packit ae235b
    case '\'': case '"':
Packit ae235b
      for (end = stream->stream + 1; end != stream->end; end++)
Packit ae235b
        if (*end == stream->stream[0] || *end == '\0' ||
Packit ae235b
            (*end == '\\' && (++end == stream->end || *end == '\0')))
Packit ae235b
          break;
Packit ae235b
Packit ae235b
      if (end != stream->end && *end)
Packit ae235b
        end++;
Packit ae235b
      break;
Packit ae235b
Packit ae235b
    case '@': case '%':
Packit ae235b
      /* stop at the first space, comma, colon or unmatched bracket.
Packit ae235b
       * deals nicely with cases like (%i, %i) or {%i: %i}.
Packit ae235b
       * Also: ] and > are never in format strings.
Packit ae235b
       */
Packit ae235b
      for (end = stream->stream + 1;
Packit ae235b
           end != stream->end && *end != '\0' && *end != ',' &&
Packit ae235b
           *end != ':' && *end != '>' && *end != ']' && !g_ascii_isspace (*end);
Packit ae235b
           end++)
Packit ae235b
Packit ae235b
        if (*end == '(' || *end == '{')
Packit ae235b
          brackets++;
Packit ae235b
Packit ae235b
        else if ((*end == ')' || *end == '}') && !brackets--)
Packit ae235b
          break;
Packit ae235b
Packit ae235b
      break;
Packit ae235b
Packit ae235b
    default:
Packit ae235b
      end = stream->stream + 1;
Packit ae235b
      break;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  stream->this = stream->stream;
Packit ae235b
  stream->stream = end;
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
token_stream_next (TokenStream *stream)
Packit ae235b
{
Packit ae235b
  stream->this = NULL;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
token_stream_peek (TokenStream *stream,
Packit ae235b
                   gchar        first_char)
Packit ae235b
{
Packit ae235b
  if (!token_stream_prepare (stream))
Packit ae235b
    return FALSE;
Packit ae235b
Packit ae235b
  return stream->this[0] == first_char;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
token_stream_peek2 (TokenStream *stream,
Packit ae235b
                    gchar        first_char,
Packit ae235b
                    gchar        second_char)
Packit ae235b
{
Packit ae235b
  if (!token_stream_prepare (stream))
Packit ae235b
    return FALSE;
Packit ae235b
Packit ae235b
  return stream->this[0] == first_char &&
Packit ae235b
         stream->this[1] == second_char;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
token_stream_is_keyword (TokenStream *stream)
Packit ae235b
{
Packit ae235b
  if (!token_stream_prepare (stream))
Packit ae235b
    return FALSE;
Packit ae235b
Packit ae235b
  return g_ascii_isalpha (stream->this[0]) &&
Packit ae235b
         g_ascii_isalpha (stream->this[1]);
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
token_stream_is_numeric (TokenStream *stream)
Packit ae235b
{
Packit ae235b
  if (!token_stream_prepare (stream))
Packit ae235b
    return FALSE;
Packit ae235b
Packit ae235b
  return (g_ascii_isdigit (stream->this[0]) ||
Packit ae235b
          stream->this[0] == '-' ||
Packit ae235b
          stream->this[0] == '+' ||
Packit ae235b
          stream->this[0] == '.');
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
token_stream_peek_string (TokenStream *stream,
Packit ae235b
                          const gchar *token)
Packit ae235b
{
Packit ae235b
  gint length = strlen (token);
Packit ae235b
Packit ae235b
  return token_stream_prepare (stream) &&
Packit ae235b
         stream->stream - stream->this == length &&
Packit ae235b
         memcmp (stream->this, token, length) == 0;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
token_stream_consume (TokenStream *stream,
Packit ae235b
                      const gchar *token)
Packit ae235b
{
Packit ae235b
  if (!token_stream_peek_string (stream, token))
Packit ae235b
    return FALSE;
Packit ae235b
Packit ae235b
  token_stream_next (stream);
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
token_stream_require (TokenStream  *stream,
Packit ae235b
                      const gchar  *token,
Packit ae235b
                      const gchar  *purpose,
Packit ae235b
                      GError      **error)
Packit ae235b
{
Packit ae235b
Packit ae235b
  if (!token_stream_consume (stream, token))
Packit ae235b
    {
Packit ae235b
      token_stream_set_error (stream, error, FALSE,
Packit ae235b
                              G_VARIANT_PARSE_ERROR_UNEXPECTED_TOKEN,
Packit ae235b
                              "expected '%s'%s", token, purpose);
Packit ae235b
      return FALSE;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
token_stream_assert (TokenStream *stream,
Packit ae235b
                     const gchar *token)
Packit ae235b
{
Packit ae235b
  gboolean correct_token;
Packit ae235b
Packit ae235b
  correct_token = token_stream_consume (stream, token);
Packit ae235b
  g_assert (correct_token);
Packit ae235b
}
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
token_stream_get (TokenStream *stream)
Packit ae235b
{
Packit ae235b
  gchar *result;
Packit ae235b
Packit ae235b
  if (!token_stream_prepare (stream))
Packit ae235b
    return NULL;
Packit ae235b
Packit ae235b
  result = g_strndup (stream->this, stream->stream - stream->this);
Packit ae235b
Packit ae235b
  return result;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
token_stream_start_ref (TokenStream *stream,
Packit ae235b
                        SourceRef   *ref)
Packit ae235b
{
Packit ae235b
  token_stream_prepare (stream);
Packit ae235b
  ref->start = stream->this - stream->start;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
token_stream_end_ref (TokenStream *stream,
Packit ae235b
                      SourceRef   *ref)
Packit ae235b
{
Packit ae235b
  ref->end = stream->stream - stream->start;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
pattern_copy (gchar       **out,
Packit ae235b
              const gchar **in)
Packit ae235b
{
Packit ae235b
  gint brackets = 0;
Packit ae235b
Packit ae235b
  while (**in == 'a' || **in == 'm' || **in == 'M')
Packit ae235b
    *(*out)++ = *(*in)++;
Packit ae235b
Packit ae235b
  do
Packit ae235b
    {
Packit ae235b
      if (**in == '(' || **in == '{')
Packit ae235b
        brackets++;
Packit ae235b
Packit ae235b
      else if (**in == ')' || **in == '}')
Packit ae235b
        brackets--;
Packit ae235b
Packit ae235b
      *(*out)++ = *(*in)++;
Packit ae235b
    }
Packit ae235b
  while (brackets);
Packit ae235b
}
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
pattern_coalesce (const gchar *left,
Packit ae235b
                  const gchar *right)
Packit ae235b
{
Packit ae235b
  gchar *result;
Packit ae235b
  gchar *out;
Packit ae235b
Packit ae235b
  /* the length of the output is loosely bound by the sum of the input
Packit ae235b
   * lengths, not simply the greater of the two lengths.
Packit ae235b
   *
Packit ae235b
   *   (*(iii)) + ((iii)*) ((iii)(iii))
Packit ae235b
   *
Packit ae235b
   *      8     +    8    =  12
Packit ae235b
   */
Packit ae235b
  out = result = g_malloc (strlen (left) + strlen (right));
Packit ae235b
Packit ae235b
  while (*left && *right)
Packit ae235b
    {
Packit ae235b
      if (*left == *right)
Packit ae235b
        {
Packit ae235b
          *out++ = *left++;
Packit ae235b
          right++;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      else
Packit ae235b
        {
Packit ae235b
          const gchar **one = &left, **the_other = &right;
Packit ae235b
Packit ae235b
         again:
Packit ae235b
          if (**one == '*' && **the_other != ')')
Packit ae235b
            {
Packit ae235b
              pattern_copy (&out, the_other);
Packit ae235b
              (*one)++;
Packit ae235b
            }
Packit ae235b
Packit ae235b
          else if (**one == 'M' && **the_other == 'm')
Packit ae235b
            {
Packit ae235b
              *out++ = *(*the_other)++;
Packit ae235b
            }
Packit ae235b
Packit ae235b
          else if (**one == 'M' && **the_other != 'm')
Packit ae235b
            {
Packit ae235b
              (*one)++;
Packit ae235b
            }
Packit ae235b
Packit ae235b
          else if (**one == 'N' && strchr ("ynqiuxthd", **the_other))
Packit ae235b
            {
Packit ae235b
              *out++ = *(*the_other)++;
Packit ae235b
              (*one)++;
Packit ae235b
            }
Packit ae235b
Packit ae235b
          else if (**one == 'S' && strchr ("sog", **the_other))
Packit ae235b
            {
Packit ae235b
              *out++ = *(*the_other)++;
Packit ae235b
              (*one)++;
Packit ae235b
            }
Packit ae235b
Packit ae235b
          else if (one == &left)
Packit ae235b
            {
Packit ae235b
              one = &right, the_other = &left;
Packit ae235b
              goto again;
Packit ae235b
            }
Packit ae235b
Packit ae235b
          else
Packit ae235b
            break;
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (*left || *right)
Packit ae235b
    {
Packit ae235b
      g_free (result);
Packit ae235b
      result = NULL;
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    *out++ = '\0';
Packit ae235b
Packit ae235b
  return result;
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct _AST AST;
Packit ae235b
typedef gchar *    (*get_pattern_func)    (AST                 *ast,
Packit ae235b
                                           GError             **error);
Packit ae235b
typedef GVariant * (*get_value_func)      (AST                 *ast,
Packit ae235b
                                           const GVariantType  *type,
Packit ae235b
                                           GError             **error);
Packit ae235b
typedef GVariant * (*get_base_value_func) (AST                 *ast,
Packit ae235b
                                           const GVariantType  *type,
Packit ae235b
                                           GError             **error);
Packit ae235b
typedef void       (*free_func)           (AST                 *ast);
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  gchar *    (* get_pattern)    (AST                 *ast,
Packit ae235b
                                 GError             **error);
Packit ae235b
  GVariant * (* get_value)      (AST                 *ast,
Packit ae235b
                                 const GVariantType  *type,
Packit ae235b
                                 GError             **error);
Packit ae235b
  GVariant * (* get_base_value) (AST                 *ast,
Packit ae235b
                                 const GVariantType  *type,
Packit ae235b
                                 GError             **error);
Packit ae235b
  void       (* free)           (AST                 *ast);
Packit ae235b
} ASTClass;
Packit ae235b
Packit ae235b
struct _AST
Packit ae235b
{
Packit ae235b
  const ASTClass *class;
Packit ae235b
  SourceRef source_ref;
Packit ae235b
};
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
ast_get_pattern (AST     *ast,
Packit ae235b
                 GError **error)
Packit ae235b
{
Packit ae235b
  return ast->class->get_pattern (ast, error);
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
ast_get_value (AST                 *ast,
Packit ae235b
               const GVariantType  *type,
Packit ae235b
               GError             **error)
Packit ae235b
{
Packit ae235b
  return ast->class->get_value (ast, type, error);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
ast_free (AST *ast)
Packit ae235b
{
Packit ae235b
  ast->class->free (ast);
Packit ae235b
}
Packit ae235b
Packit ae235b
G_GNUC_PRINTF(5, 6)
Packit ae235b
static void
Packit ae235b
ast_set_error (AST          *ast,
Packit ae235b
               GError      **error,
Packit ae235b
               AST          *other_ast,
Packit ae235b
               gint          code,
Packit ae235b
               const gchar  *format,
Packit ae235b
               ...)
Packit ae235b
{
Packit ae235b
  va_list ap;
Packit ae235b
Packit ae235b
  va_start (ap, format);
Packit ae235b
  parser_set_error_va (error, &ast->source_ref,
Packit ae235b
                       other_ast ? & other_ast->source_ref : NULL,
Packit ae235b
                       code,
Packit ae235b
                       format, ap);
Packit ae235b
  va_end (ap);
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
ast_type_error (AST                 *ast,
Packit ae235b
                const GVariantType  *type,
Packit ae235b
                GError             **error)
Packit ae235b
{
Packit ae235b
  gchar *typestr;
Packit ae235b
Packit ae235b
  typestr = g_variant_type_dup_string (type);
Packit ae235b
  ast_set_error (ast, error, NULL,
Packit ae235b
                 G_VARIANT_PARSE_ERROR_TYPE_ERROR,
Packit ae235b
                 "can not parse as value of type '%s'",
Packit ae235b
                 typestr);
Packit ae235b
  g_free (typestr);
Packit ae235b
Packit ae235b
  return NULL;
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
ast_resolve (AST     *ast,
Packit ae235b
             GError **error)
Packit ae235b
{
Packit ae235b
  GVariant *value;
Packit ae235b
  gchar *pattern;
Packit ae235b
  gint i, j = 0;
Packit ae235b
Packit ae235b
  pattern = ast_get_pattern (ast, error);
Packit ae235b
Packit ae235b
  if (pattern == NULL)
Packit ae235b
    return NULL;
Packit ae235b
Packit ae235b
  /* choose reasonable defaults
Packit ae235b
   *
Packit ae235b
   *   1) favour non-maybe values where possible
Packit ae235b
   *   2) default type for strings is 's'
Packit ae235b
   *   3) default type for integers is 'i'
Packit ae235b
   */
Packit ae235b
  for (i = 0; pattern[i]; i++)
Packit ae235b
    switch (pattern[i])
Packit ae235b
      {
Packit ae235b
      case '*':
Packit ae235b
        ast_set_error (ast, error, NULL,
Packit ae235b
                       G_VARIANT_PARSE_ERROR_CANNOT_INFER_TYPE,
Packit ae235b
                       "unable to infer type");
Packit ae235b
        g_free (pattern);
Packit ae235b
        return NULL;
Packit ae235b
Packit ae235b
      case 'M':
Packit ae235b
        break;
Packit ae235b
Packit ae235b
      case 'S':
Packit ae235b
        pattern[j++] = 's';
Packit ae235b
        break;
Packit ae235b
Packit ae235b
      case 'N':
Packit ae235b
        pattern[j++] = 'i';
Packit ae235b
        break;
Packit ae235b
Packit ae235b
      default:
Packit ae235b
        pattern[j++] = pattern[i];
Packit ae235b
        break;
Packit ae235b
      }
Packit ae235b
  pattern[j++] = '\0';
Packit ae235b
Packit ae235b
  value = ast_get_value (ast, G_VARIANT_TYPE (pattern), error);
Packit ae235b
  g_free (pattern);
Packit ae235b
Packit ae235b
  return value;
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
static AST *parse (TokenStream  *stream,
Packit ae235b
                   va_list      *app,
Packit ae235b
                   GError      **error);
Packit ae235b
Packit ae235b
static void
Packit ae235b
ast_array_append (AST  ***array,
Packit ae235b
                  gint   *n_items,
Packit ae235b
                  AST    *ast)
Packit ae235b
{
Packit ae235b
  if ((*n_items & (*n_items - 1)) == 0)
Packit ae235b
    *array = g_renew (AST *, *array, *n_items ? 2 ** n_items : 1);
Packit ae235b
Packit ae235b
  (*array)[(*n_items)++] = ast;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
ast_array_free (AST  **array,
Packit ae235b
                gint   n_items)
Packit ae235b
{
Packit ae235b
  gint i;
Packit ae235b
Packit ae235b
  for (i = 0; i < n_items; i++)
Packit ae235b
    ast_free (array[i]);
Packit ae235b
  g_free (array);
Packit ae235b
}
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
ast_array_get_pattern (AST    **array,
Packit ae235b
                       gint     n_items,
Packit ae235b
                       GError **error)
Packit ae235b
{
Packit ae235b
  gchar *pattern;
Packit ae235b
  gint i;
Packit ae235b
Packit ae235b
  pattern = ast_get_pattern (array[0], error);
Packit ae235b
Packit ae235b
  if (pattern == NULL)
Packit ae235b
    return NULL;
Packit ae235b
Packit ae235b
  for (i = 1; i < n_items; i++)
Packit ae235b
    {
Packit ae235b
      gchar *tmp, *merged;
Packit ae235b
Packit ae235b
      tmp = ast_get_pattern (array[i], error);
Packit ae235b
Packit ae235b
      if (tmp == NULL)
Packit ae235b
        {
Packit ae235b
          g_free (pattern);
Packit ae235b
          return NULL;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      merged = pattern_coalesce (pattern, tmp);
Packit ae235b
      g_free (pattern);
Packit ae235b
      pattern = merged;
Packit ae235b
Packit ae235b
      if (merged == NULL)
Packit ae235b
        /* set coalescence implies pairwise coalescence (i think).
Packit ae235b
         * we should therefore be able to trace the failure to a single
Packit ae235b
         * pair of values.
Packit ae235b
         */
Packit ae235b
        {
Packit ae235b
          int j = 0;
Packit ae235b
Packit ae235b
          while (TRUE)
Packit ae235b
            {
Packit ae235b
              gchar *tmp2;
Packit ae235b
              gchar *m;
Packit ae235b
Packit ae235b
              /* if 'j' reaches 'i' then we failed to find the pair */
Packit ae235b
              g_assert (j < i);
Packit ae235b
Packit ae235b
              tmp2 = ast_get_pattern (array[j], NULL);
Packit ae235b
              g_assert (tmp2 != NULL);
Packit ae235b
Packit ae235b
              m = pattern_coalesce (tmp, tmp2);
Packit ae235b
              g_free (tmp2);
Packit ae235b
              g_free (m);
Packit ae235b
Packit ae235b
              if (m == NULL)
Packit ae235b
                {
Packit ae235b
                  /* we found a conflict between 'i' and 'j'.
Packit ae235b
                   *
Packit ae235b
                   * report the error.  note: 'j' is first.
Packit ae235b
                   */
Packit ae235b
                  ast_set_error (array[j], error, array[i],
Packit ae235b
                                 G_VARIANT_PARSE_ERROR_NO_COMMON_TYPE,
Packit ae235b
                                 "unable to find a common type");
Packit ae235b
                  g_free (tmp);
Packit ae235b
                  return NULL;
Packit ae235b
                }
Packit ae235b
Packit ae235b
              j++;
Packit ae235b
            }
Packit ae235b
Packit ae235b
        }
Packit ae235b
Packit ae235b
      g_free (tmp);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return pattern;
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  AST ast;
Packit ae235b
Packit ae235b
  AST *child;
Packit ae235b
} Maybe;
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
maybe_get_pattern (AST     *ast,
Packit ae235b
                   GError **error)
Packit ae235b
{
Packit ae235b
  Maybe *maybe = (Maybe *) ast;
Packit ae235b
Packit ae235b
  if (maybe->child != NULL)
Packit ae235b
    {
Packit ae235b
      gchar *child_pattern;
Packit ae235b
      gchar *pattern;
Packit ae235b
Packit ae235b
      child_pattern = ast_get_pattern (maybe->child, error);
Packit ae235b
Packit ae235b
      if (child_pattern == NULL)
Packit ae235b
        return NULL;
Packit ae235b
Packit ae235b
      pattern = g_strdup_printf ("m%s", child_pattern);
Packit ae235b
      g_free (child_pattern);
Packit ae235b
Packit ae235b
      return pattern;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return g_strdup ("m*");
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
maybe_get_value (AST                 *ast,
Packit ae235b
                 const GVariantType  *type,
Packit ae235b
                 GError             **error)
Packit ae235b
{
Packit ae235b
  Maybe *maybe = (Maybe *) ast;
Packit ae235b
  GVariant *value;
Packit ae235b
Packit ae235b
  if (!g_variant_type_is_maybe (type))
Packit ae235b
    return ast_type_error (ast, type, error);
Packit ae235b
Packit ae235b
  type = g_variant_type_element (type);
Packit ae235b
Packit ae235b
  if (maybe->child)
Packit ae235b
    {
Packit ae235b
      value = ast_get_value (maybe->child, type, error);
Packit ae235b
Packit ae235b
      if (value == NULL)
Packit ae235b
        return NULL;
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    value = NULL;
Packit ae235b
Packit ae235b
  return g_variant_new_maybe (type, value);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
maybe_free (AST *ast)
Packit ae235b
{
Packit ae235b
  Maybe *maybe = (Maybe *) ast;
Packit ae235b
Packit ae235b
  if (maybe->child != NULL)
Packit ae235b
    ast_free (maybe->child);
Packit ae235b
Packit ae235b
  g_slice_free (Maybe, maybe);
Packit ae235b
}
Packit ae235b
Packit ae235b
static AST *
Packit ae235b
maybe_parse (TokenStream  *stream,
Packit ae235b
             va_list      *app,
Packit ae235b
             GError      **error)
Packit ae235b
{
Packit ae235b
  static const ASTClass maybe_class = {
Packit ae235b
    maybe_get_pattern,
Packit ae235b
    maybe_get_value, NULL,
Packit ae235b
    maybe_free
Packit ae235b
  };
Packit ae235b
  AST *child = NULL;
Packit ae235b
  Maybe *maybe;
Packit ae235b
Packit ae235b
  if (token_stream_consume (stream, "just"))
Packit ae235b
    {
Packit ae235b
      child = parse (stream, app, error);
Packit ae235b
      if (child == NULL)
Packit ae235b
        return NULL;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  else if (!token_stream_consume (stream, "nothing"))
Packit ae235b
    {
Packit ae235b
      token_stream_set_error (stream, error, TRUE,
Packit ae235b
                              G_VARIANT_PARSE_ERROR_UNKNOWN_KEYWORD,
Packit ae235b
                              "unknown keyword");
Packit ae235b
      return NULL;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  maybe = g_slice_new (Maybe);
Packit ae235b
  maybe->ast.class = &maybe_class;
Packit ae235b
  maybe->child = child;
Packit ae235b
Packit ae235b
  return (AST *) maybe;
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
maybe_wrapper (AST                 *ast,
Packit ae235b
               const GVariantType  *type,
Packit ae235b
               GError             **error)
Packit ae235b
{
Packit ae235b
  const GVariantType *t;
Packit ae235b
  GVariant *value;
Packit ae235b
  int depth;
Packit ae235b
Packit ae235b
  for (depth = 0, t = type;
Packit ae235b
       g_variant_type_is_maybe (t);
Packit ae235b
       depth++, t = g_variant_type_element (t));
Packit ae235b
Packit ae235b
  value = ast->class->get_base_value (ast, t, error);
Packit ae235b
Packit ae235b
  if (value == NULL)
Packit ae235b
    return NULL;
Packit ae235b
Packit ae235b
  while (depth--)
Packit ae235b
    value = g_variant_new_maybe (NULL, value);
Packit ae235b
Packit ae235b
  return value;
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  AST ast;
Packit ae235b
Packit ae235b
  AST **children;
Packit ae235b
  gint n_children;
Packit ae235b
} Array;
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
array_get_pattern (AST     *ast,
Packit ae235b
                   GError **error)
Packit ae235b
{
Packit ae235b
  Array *array = (Array *) ast;
Packit ae235b
  gchar *pattern;
Packit ae235b
  gchar *result;
Packit ae235b
Packit ae235b
  if (array->n_children == 0)
Packit ae235b
    return g_strdup ("Ma*");
Packit ae235b
Packit ae235b
  pattern = ast_array_get_pattern (array->children, array->n_children, error);
Packit ae235b
Packit ae235b
  if (pattern == NULL)
Packit ae235b
    return NULL;
Packit ae235b
Packit ae235b
  result = g_strdup_printf ("Ma%s", pattern);
Packit ae235b
  g_free (pattern);
Packit ae235b
Packit ae235b
  return result;
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
array_get_value (AST                 *ast,
Packit ae235b
                 const GVariantType  *type,
Packit ae235b
                 GError             **error)
Packit ae235b
{
Packit ae235b
  Array *array = (Array *) ast;
Packit ae235b
  const GVariantType *childtype;
Packit ae235b
  GVariantBuilder builder;
Packit ae235b
  gint i;
Packit ae235b
Packit ae235b
  if (!g_variant_type_is_array (type))
Packit ae235b
    return ast_type_error (ast, type, error);
Packit ae235b
Packit ae235b
  g_variant_builder_init (&builder, type);
Packit ae235b
  childtype = g_variant_type_element (type);
Packit ae235b
Packit ae235b
  for (i = 0; i < array->n_children; i++)
Packit ae235b
    {
Packit ae235b
      GVariant *child;
Packit ae235b
Packit ae235b
      if (!(child = ast_get_value (array->children[i], childtype, error)))
Packit ae235b
        {
Packit ae235b
          g_variant_builder_clear (&builder);
Packit ae235b
          return NULL;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      g_variant_builder_add_value (&builder, child);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return g_variant_builder_end (&builder);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
array_free (AST *ast)
Packit ae235b
{
Packit ae235b
  Array *array = (Array *) ast;
Packit ae235b
Packit ae235b
  ast_array_free (array->children, array->n_children);
Packit ae235b
  g_slice_free (Array, array);
Packit ae235b
}
Packit ae235b
Packit ae235b
static AST *
Packit ae235b
array_parse (TokenStream  *stream,
Packit ae235b
             va_list      *app,
Packit ae235b
             GError      **error)
Packit ae235b
{
Packit ae235b
  static const ASTClass array_class = {
Packit ae235b
    array_get_pattern,
Packit ae235b
    maybe_wrapper, array_get_value,
Packit ae235b
    array_free
Packit ae235b
  };
Packit ae235b
  gboolean need_comma = FALSE;
Packit ae235b
  Array *array;
Packit ae235b
Packit ae235b
  array = g_slice_new (Array);
Packit ae235b
  array->ast.class = &array_class;
Packit ae235b
  array->children = NULL;
Packit ae235b
  array->n_children = 0;
Packit ae235b
Packit ae235b
  token_stream_assert (stream, "[");
Packit ae235b
  while (!token_stream_consume (stream, "]"))
Packit ae235b
    {
Packit ae235b
      AST *child;
Packit ae235b
Packit ae235b
      if (need_comma &&
Packit ae235b
          !token_stream_require (stream, ",",
Packit ae235b
                                 " or ']' to follow array element",
Packit ae235b
                                 error))
Packit ae235b
        goto error;
Packit ae235b
Packit ae235b
      child = parse (stream, app, error);
Packit ae235b
Packit ae235b
      if (!child)
Packit ae235b
        goto error;
Packit ae235b
Packit ae235b
      ast_array_append (&array->children, &array->n_children, child);
Packit ae235b
      need_comma = TRUE;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return (AST *) array;
Packit ae235b
Packit ae235b
 error:
Packit ae235b
  ast_array_free (array->children, array->n_children);
Packit ae235b
  g_slice_free (Array, array);
Packit ae235b
Packit ae235b
  return NULL;
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  AST ast;
Packit ae235b
Packit ae235b
  AST **children;
Packit ae235b
  gint n_children;
Packit ae235b
} Tuple;
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
tuple_get_pattern (AST     *ast,
Packit ae235b
                   GError **error)
Packit ae235b
{
Packit ae235b
  Tuple *tuple = (Tuple *) ast;
Packit ae235b
  gchar *result = NULL;
Packit ae235b
  gchar **parts;
Packit ae235b
  gint i;
Packit ae235b
Packit ae235b
  parts = g_new (gchar *, tuple->n_children + 4);
Packit ae235b
  parts[tuple->n_children + 1] = (gchar *) ")";
Packit ae235b
  parts[tuple->n_children + 2] = NULL;
Packit ae235b
  parts[0] = (gchar *) "M(";
Packit ae235b
Packit ae235b
  for (i = 0; i < tuple->n_children; i++)
Packit ae235b
    if (!(parts[i + 1] = ast_get_pattern (tuple->children[i], error)))
Packit ae235b
      break;
Packit ae235b
Packit ae235b
  if (i == tuple->n_children)
Packit ae235b
    result = g_strjoinv ("", parts);
Packit ae235b
Packit ae235b
  /* parts[0] should not be freed */
Packit ae235b
  while (i)
Packit ae235b
    g_free (parts[i--]);
Packit ae235b
  g_free (parts);
Packit ae235b
Packit ae235b
  return result;
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
tuple_get_value (AST                 *ast,
Packit ae235b
                 const GVariantType  *type,
Packit ae235b
                 GError             **error)
Packit ae235b
{
Packit ae235b
  Tuple *tuple = (Tuple *) ast;
Packit ae235b
  const GVariantType *childtype;
Packit ae235b
  GVariantBuilder builder;
Packit ae235b
  gint i;
Packit ae235b
Packit ae235b
  if (!g_variant_type_is_tuple (type))
Packit ae235b
    return ast_type_error (ast, type, error);
Packit ae235b
Packit ae235b
  g_variant_builder_init (&builder, type);
Packit ae235b
  childtype = g_variant_type_first (type);
Packit ae235b
Packit ae235b
  for (i = 0; i < tuple->n_children; i++)
Packit ae235b
    {
Packit ae235b
      GVariant *child;
Packit ae235b
Packit ae235b
      if (childtype == NULL)
Packit ae235b
        {
Packit ae235b
          g_variant_builder_clear (&builder);
Packit ae235b
          return ast_type_error (ast, type, error);
Packit ae235b
        }
Packit ae235b
Packit ae235b
      if (!(child = ast_get_value (tuple->children[i], childtype, error)))
Packit ae235b
        {
Packit ae235b
          g_variant_builder_clear (&builder);
Packit ae235b
          return FALSE;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      g_variant_builder_add_value (&builder, child);
Packit ae235b
      childtype = g_variant_type_next (childtype);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (childtype != NULL)
Packit ae235b
    {
Packit ae235b
      g_variant_builder_clear (&builder);
Packit ae235b
      return ast_type_error (ast, type, error);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return g_variant_builder_end (&builder);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
tuple_free (AST *ast)
Packit ae235b
{
Packit ae235b
  Tuple *tuple = (Tuple *) ast;
Packit ae235b
Packit ae235b
  ast_array_free (tuple->children, tuple->n_children);
Packit ae235b
  g_slice_free (Tuple, tuple);
Packit ae235b
}
Packit ae235b
Packit ae235b
static AST *
Packit ae235b
tuple_parse (TokenStream  *stream,
Packit ae235b
             va_list      *app,
Packit ae235b
             GError      **error)
Packit ae235b
{
Packit ae235b
  static const ASTClass tuple_class = {
Packit ae235b
    tuple_get_pattern,
Packit ae235b
    maybe_wrapper, tuple_get_value,
Packit ae235b
    tuple_free
Packit ae235b
  };
Packit ae235b
  gboolean need_comma = FALSE;
Packit ae235b
  gboolean first = TRUE;
Packit ae235b
  Tuple *tuple;
Packit ae235b
Packit ae235b
  tuple = g_slice_new (Tuple);
Packit ae235b
  tuple->ast.class = &tuple_class;
Packit ae235b
  tuple->children = NULL;
Packit ae235b
  tuple->n_children = 0;
Packit ae235b
Packit ae235b
  token_stream_assert (stream, "(");
Packit ae235b
  while (!token_stream_consume (stream, ")"))
Packit ae235b
    {
Packit ae235b
      AST *child;
Packit ae235b
Packit ae235b
      if (need_comma &&
Packit ae235b
          !token_stream_require (stream, ",",
Packit ae235b
                                 " or ')' to follow tuple element",
Packit ae235b
                                 error))
Packit ae235b
        goto error;
Packit ae235b
Packit ae235b
      child = parse (stream, app, error);
Packit ae235b
Packit ae235b
      if (!child)
Packit ae235b
        goto error;
Packit ae235b
Packit ae235b
      ast_array_append (&tuple->children, &tuple->n_children, child);
Packit ae235b
Packit ae235b
      /* the first time, we absolutely require a comma, so grab it here
Packit ae235b
       * and leave need_comma = FALSE so that the code above doesn't
Packit ae235b
       * require a second comma.
Packit ae235b
       *
Packit ae235b
       * the second and remaining times, we set need_comma = TRUE.
Packit ae235b
       */
Packit ae235b
      if (first)
Packit ae235b
        {
Packit ae235b
          if (!token_stream_require (stream, ",",
Packit ae235b
                                     " after first tuple element", error))
Packit ae235b
            goto error;
Packit ae235b
Packit ae235b
          first = FALSE;
Packit ae235b
        }
Packit ae235b
      else
Packit ae235b
        need_comma = TRUE;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return (AST *) tuple;
Packit ae235b
Packit ae235b
 error:
Packit ae235b
  ast_array_free (tuple->children, tuple->n_children);
Packit ae235b
  g_slice_free (Tuple, tuple);
Packit ae235b
Packit ae235b
  return NULL;
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  AST ast;
Packit ae235b
Packit ae235b
  AST *value;
Packit ae235b
} Variant;
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
variant_get_pattern (AST     *ast,
Packit ae235b
                     GError **error)
Packit ae235b
{
Packit ae235b
  return g_strdup ("Mv");
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
variant_get_value (AST                 *ast,
Packit ae235b
                   const GVariantType  *type,
Packit ae235b
                   GError             **error)
Packit ae235b
{
Packit ae235b
  Variant *variant = (Variant *) ast;
Packit ae235b
  GVariant *child;
Packit ae235b
Packit ae235b
  if (!g_variant_type_equal (type, G_VARIANT_TYPE_VARIANT))
Packit ae235b
    return ast_type_error (ast, type, error);
Packit ae235b
Packit ae235b
  child = ast_resolve (variant->value, error);
Packit ae235b
Packit ae235b
  if (child == NULL)
Packit ae235b
    return NULL;
Packit ae235b
Packit ae235b
  return g_variant_new_variant (child);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
variant_free (AST *ast)
Packit ae235b
{
Packit ae235b
  Variant *variant = (Variant *) ast;
Packit ae235b
Packit ae235b
  ast_free (variant->value);
Packit ae235b
  g_slice_free (Variant, variant);
Packit ae235b
}
Packit ae235b
Packit ae235b
static AST *
Packit ae235b
variant_parse (TokenStream  *stream,
Packit ae235b
               va_list      *app,
Packit ae235b
               GError      **error)
Packit ae235b
{
Packit ae235b
  static const ASTClass variant_class = {
Packit ae235b
    variant_get_pattern,
Packit ae235b
    maybe_wrapper, variant_get_value,
Packit ae235b
    variant_free
Packit ae235b
  };
Packit ae235b
  Variant *variant;
Packit ae235b
  AST *value;
Packit ae235b
Packit ae235b
  token_stream_assert (stream, "<");
Packit ae235b
  value = parse (stream, app, error);
Packit ae235b
Packit ae235b
  if (!value)
Packit ae235b
    return NULL;
Packit ae235b
Packit ae235b
  if (!token_stream_require (stream, ">", " to follow variant value", error))
Packit ae235b
    {
Packit ae235b
      ast_free (value);
Packit ae235b
      return NULL;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  variant = g_slice_new (Variant);
Packit ae235b
  variant->ast.class = &variant_class;
Packit ae235b
  variant->value = value;
Packit ae235b
Packit ae235b
  return (AST *) variant;
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  AST ast;
Packit ae235b
Packit ae235b
  AST **keys;
Packit ae235b
  AST **values;
Packit ae235b
  gint n_children;
Packit ae235b
} Dictionary;
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
dictionary_get_pattern (AST     *ast,
Packit ae235b
                        GError **error)
Packit ae235b
{
Packit ae235b
  Dictionary *dict = (Dictionary *) ast;
Packit ae235b
  gchar *value_pattern;
Packit ae235b
  gchar *key_pattern;
Packit ae235b
  gchar key_char;
Packit ae235b
  gchar *result;
Packit ae235b
Packit ae235b
  if (dict->n_children == 0)
Packit ae235b
    return g_strdup ("Ma{**}");
Packit ae235b
Packit ae235b
  key_pattern = ast_array_get_pattern (dict->keys,
Packit ae235b
                                       abs (dict->n_children),
Packit ae235b
                                       error);
Packit ae235b
Packit ae235b
  if (key_pattern == NULL)
Packit ae235b
    return NULL;
Packit ae235b
Packit ae235b
  /* we can not have maybe keys */
Packit ae235b
  if (key_pattern[0] == 'M')
Packit ae235b
    key_char = key_pattern[1];
Packit ae235b
  else
Packit ae235b
    key_char = key_pattern[0];
Packit ae235b
Packit ae235b
  g_free (key_pattern);
Packit ae235b
Packit ae235b
  /* the basic types,
Packit ae235b
   * plus undetermined number type and undetermined string type.
Packit ae235b
   */
Packit ae235b
  if (!strchr ("bynqiuxthdsogNS", key_char))
Packit ae235b
    {
Packit ae235b
      ast_set_error (ast, error, NULL,
Packit ae235b
                     G_VARIANT_PARSE_ERROR_BASIC_TYPE_EXPECTED,
Packit ae235b
                     "dictionary keys must have basic types");
Packit ae235b
      return NULL;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  value_pattern = ast_get_pattern (dict->values[0], error);
Packit ae235b
Packit ae235b
  if (value_pattern == NULL)
Packit ae235b
    return NULL;
Packit ae235b
Packit ae235b
  result = g_strdup_printf ("M%s{%c%s}",
Packit ae235b
                            dict->n_children > 0 ? "a" : "",
Packit ae235b
                            key_char, value_pattern);
Packit ae235b
  g_free (value_pattern);
Packit ae235b
Packit ae235b
  return result;
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
dictionary_get_value (AST                 *ast,
Packit ae235b
                      const GVariantType  *type,
Packit ae235b
                      GError             **error)
Packit ae235b
{
Packit ae235b
  Dictionary *dict = (Dictionary *) ast;
Packit ae235b
Packit ae235b
  if (dict->n_children == -1)
Packit ae235b
    {
Packit ae235b
      const GVariantType *subtype;
Packit ae235b
      GVariantBuilder builder;
Packit ae235b
      GVariant *subvalue;
Packit ae235b
Packit ae235b
      if (!g_variant_type_is_dict_entry (type))
Packit ae235b
        return ast_type_error (ast, type, error);
Packit ae235b
Packit ae235b
      g_variant_builder_init (&builder, type);
Packit ae235b
Packit ae235b
      subtype = g_variant_type_key (type);
Packit ae235b
      if (!(subvalue = ast_get_value (dict->keys[0], subtype, error)))
Packit ae235b
        {
Packit ae235b
          g_variant_builder_clear (&builder);
Packit ae235b
          return NULL;
Packit ae235b
        }
Packit ae235b
      g_variant_builder_add_value (&builder, subvalue);
Packit ae235b
Packit ae235b
      subtype = g_variant_type_value (type);
Packit ae235b
      if (!(subvalue = ast_get_value (dict->values[0], subtype, error)))
Packit ae235b
        {
Packit ae235b
          g_variant_builder_clear (&builder);
Packit ae235b
          return NULL;
Packit ae235b
        }
Packit ae235b
      g_variant_builder_add_value (&builder, subvalue);
Packit ae235b
Packit ae235b
      return g_variant_builder_end (&builder);
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      const GVariantType *entry, *key, *val;
Packit ae235b
      GVariantBuilder builder;
Packit ae235b
      gint i;
Packit ae235b
Packit ae235b
      if (!g_variant_type_is_subtype_of (type, G_VARIANT_TYPE_DICTIONARY))
Packit ae235b
        return ast_type_error (ast, type, error);
Packit ae235b
Packit ae235b
      entry = g_variant_type_element (type);
Packit ae235b
      key = g_variant_type_key (entry);
Packit ae235b
      val = g_variant_type_value (entry);
Packit ae235b
Packit ae235b
      g_variant_builder_init (&builder, type);
Packit ae235b
Packit ae235b
      for (i = 0; i < dict->n_children; i++)
Packit ae235b
        {
Packit ae235b
          GVariant *subvalue;
Packit ae235b
Packit ae235b
          g_variant_builder_open (&builder, entry);
Packit ae235b
Packit ae235b
          if (!(subvalue = ast_get_value (dict->keys[i], key, error)))
Packit ae235b
            {
Packit ae235b
              g_variant_builder_clear (&builder);
Packit ae235b
              return NULL;
Packit ae235b
            }
Packit ae235b
          g_variant_builder_add_value (&builder, subvalue);
Packit ae235b
Packit ae235b
          if (!(subvalue = ast_get_value (dict->values[i], val, error)))
Packit ae235b
            {
Packit ae235b
              g_variant_builder_clear (&builder);
Packit ae235b
              return NULL;
Packit ae235b
            }
Packit ae235b
          g_variant_builder_add_value (&builder, subvalue);
Packit ae235b
          g_variant_builder_close (&builder);
Packit ae235b
        }
Packit ae235b
Packit ae235b
      return g_variant_builder_end (&builder);
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
dictionary_free (AST *ast)
Packit ae235b
{
Packit ae235b
  Dictionary *dict = (Dictionary *) ast;
Packit ae235b
  gint n_children;
Packit ae235b
Packit ae235b
  if (dict->n_children > -1)
Packit ae235b
    n_children = dict->n_children;
Packit ae235b
  else
Packit ae235b
    n_children = 1;
Packit ae235b
Packit ae235b
  ast_array_free (dict->keys, n_children);
Packit ae235b
  ast_array_free (dict->values, n_children);
Packit ae235b
  g_slice_free (Dictionary, dict);
Packit ae235b
}
Packit ae235b
Packit ae235b
static AST *
Packit ae235b
dictionary_parse (TokenStream  *stream,
Packit ae235b
                  va_list      *app,
Packit ae235b
                  GError      **error)
Packit ae235b
{
Packit ae235b
  static const ASTClass dictionary_class = {
Packit ae235b
    dictionary_get_pattern,
Packit ae235b
    maybe_wrapper, dictionary_get_value,
Packit ae235b
    dictionary_free
Packit ae235b
  };
Packit ae235b
  gint n_keys, n_values;
Packit ae235b
  gboolean only_one;
Packit ae235b
  Dictionary *dict;
Packit ae235b
  AST *first;
Packit ae235b
Packit ae235b
  dict = g_slice_new (Dictionary);
Packit ae235b
  dict->ast.class = &dictionary_class;
Packit ae235b
  dict->keys = NULL;
Packit ae235b
  dict->values = NULL;
Packit ae235b
  n_keys = n_values = 0;
Packit ae235b
Packit ae235b
  token_stream_assert (stream, "{");
Packit ae235b
Packit ae235b
  if (token_stream_consume (stream, "}"))
Packit ae235b
    {
Packit ae235b
      dict->n_children = 0;
Packit ae235b
      return (AST *) dict;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if ((first = parse (stream, app, error)) == NULL)
Packit ae235b
    goto error;
Packit ae235b
Packit ae235b
  ast_array_append (&dict->keys, &n_keys, first);
Packit ae235b
Packit ae235b
  only_one = token_stream_consume (stream, ",");
Packit ae235b
  if (!only_one &&
Packit ae235b
      !token_stream_require (stream, ":",
Packit ae235b
                             " or ',' to follow dictionary entry key",
Packit ae235b
                             error))
Packit ae235b
    goto error;
Packit ae235b
Packit ae235b
  if ((first = parse (stream, app, error)) == NULL)
Packit ae235b
    goto error;
Packit ae235b
Packit ae235b
  ast_array_append (&dict->values, &n_values, first);
Packit ae235b
Packit ae235b
  if (only_one)
Packit ae235b
    {
Packit ae235b
      if (!token_stream_require (stream, "}", " at end of dictionary entry",
Packit ae235b
                                 error))
Packit ae235b
        goto error;
Packit ae235b
Packit ae235b
      g_assert (n_keys == 1 && n_values == 1);
Packit ae235b
      dict->n_children = -1;
Packit ae235b
Packit ae235b
      return (AST *) dict;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  while (!token_stream_consume (stream, "}"))
Packit ae235b
    {
Packit ae235b
      AST *child;
Packit ae235b
Packit ae235b
      if (!token_stream_require (stream, ",",
Packit ae235b
                                 " or '}' to follow dictionary entry", error))
Packit ae235b
        goto error;
Packit ae235b
Packit ae235b
      child = parse (stream, app, error);
Packit ae235b
Packit ae235b
      if (!child)
Packit ae235b
        goto error;
Packit ae235b
Packit ae235b
      ast_array_append (&dict->keys, &n_keys, child);
Packit ae235b
Packit ae235b
      if (!token_stream_require (stream, ":",
Packit ae235b
                                 " to follow dictionary entry key", error))
Packit ae235b
        goto error;
Packit ae235b
Packit ae235b
      child = parse (stream, app, error);
Packit ae235b
Packit ae235b
      if (!child)
Packit ae235b
        goto error;
Packit ae235b
Packit ae235b
      ast_array_append (&dict->values, &n_values, child);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  g_assert (n_keys == n_values);
Packit ae235b
  dict->n_children = n_keys;
Packit ae235b
Packit ae235b
  return (AST *) dict;
Packit ae235b
Packit ae235b
 error:
Packit ae235b
  ast_array_free (dict->keys, n_keys);
Packit ae235b
  ast_array_free (dict->values, n_values);
Packit ae235b
  g_slice_free (Dictionary, dict);
Packit ae235b
Packit ae235b
  return NULL;
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  AST ast;
Packit ae235b
  gchar *string;
Packit ae235b
} String;
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
string_get_pattern (AST     *ast,
Packit ae235b
                    GError **error)
Packit ae235b
{
Packit ae235b
  return g_strdup ("MS");
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
string_get_value (AST                 *ast,
Packit ae235b
                  const GVariantType  *type,
Packit ae235b
                  GError             **error)
Packit ae235b
{
Packit ae235b
  String *string = (String *) ast;
Packit ae235b
Packit ae235b
  if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
Packit ae235b
    return g_variant_new_string (string->string);
Packit ae235b
Packit ae235b
  else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
Packit ae235b
    {
Packit ae235b
      if (!g_variant_is_object_path (string->string))
Packit ae235b
        {
Packit ae235b
          ast_set_error (ast, error, NULL,
Packit ae235b
                         G_VARIANT_PARSE_ERROR_INVALID_OBJECT_PATH,
Packit ae235b
                         "not a valid object path");
Packit ae235b
          return NULL;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      return g_variant_new_object_path (string->string);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
Packit ae235b
    {
Packit ae235b
      if (!g_variant_is_signature (string->string))
Packit ae235b
        {
Packit ae235b
          ast_set_error (ast, error, NULL,
Packit ae235b
                         G_VARIANT_PARSE_ERROR_INVALID_SIGNATURE,
Packit ae235b
                         "not a valid signature");
Packit ae235b
          return NULL;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      return g_variant_new_signature (string->string);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  else
Packit ae235b
    return ast_type_error (ast, type, error);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
string_free (AST *ast)
Packit ae235b
{
Packit ae235b
  String *string = (String *) ast;
Packit ae235b
Packit ae235b
  g_free (string->string);
Packit ae235b
  g_slice_free (String, string);
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
unicode_unescape (const gchar  *src,
Packit ae235b
                  gint         *src_ofs,
Packit ae235b
                  gchar        *dest,
Packit ae235b
                  gint         *dest_ofs,
Packit ae235b
                  gint          length,
Packit ae235b
                  SourceRef    *ref,
Packit ae235b
                  GError      **error)
Packit ae235b
{
Packit ae235b
  gchar buffer[9];
Packit ae235b
  guint64 value;
Packit ae235b
  gchar *end;
Packit ae235b
Packit ae235b
  (*src_ofs)++;
Packit ae235b
Packit ae235b
  g_assert (length < sizeof (buffer));
Packit ae235b
  strncpy (buffer, src + *src_ofs, length);
Packit ae235b
  buffer[length] = '\0';
Packit ae235b
Packit ae235b
  value = g_ascii_strtoull (buffer, &end, 0x10);
Packit ae235b
Packit ae235b
  if (value == 0 || end != buffer + length)
Packit ae235b
    {
Packit ae235b
      parser_set_error (error, ref, NULL,
Packit ae235b
                        G_VARIANT_PARSE_ERROR_INVALID_CHARACTER,
Packit ae235b
                        "invalid %d-character unicode escape", length);
Packit ae235b
      return FALSE;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  g_assert (value <= G_MAXUINT32);
Packit ae235b
Packit ae235b
  *dest_ofs += g_unichar_to_utf8 (value, dest + *dest_ofs);
Packit ae235b
  *src_ofs += length;
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static AST *
Packit ae235b
string_parse (TokenStream  *stream,
Packit ae235b
              va_list      *app,
Packit ae235b
              GError      **error)
Packit ae235b
{
Packit ae235b
  static const ASTClass string_class = {
Packit ae235b
    string_get_pattern,
Packit ae235b
    maybe_wrapper, string_get_value,
Packit ae235b
    string_free
Packit ae235b
  };
Packit ae235b
  String *string;
Packit ae235b
  SourceRef ref;
Packit ae235b
  gchar *token;
Packit ae235b
  gsize length;
Packit ae235b
  gchar quote;
Packit ae235b
  gchar *str;
Packit ae235b
  gint i, j;
Packit ae235b
Packit ae235b
  token_stream_start_ref (stream, &ref;;
Packit ae235b
  token = token_stream_get (stream);
Packit ae235b
  token_stream_end_ref (stream, &ref;;
Packit ae235b
  length = strlen (token);
Packit ae235b
  quote = token[0];
Packit ae235b
Packit ae235b
  str = g_malloc (length);
Packit ae235b
  g_assert (quote == '"' || quote == '\'');
Packit ae235b
  j = 0;
Packit ae235b
  i = 1;
Packit ae235b
  while (token[i] != quote)
Packit ae235b
    switch (token[i])
Packit ae235b
      {
Packit ae235b
      case '\0':
Packit ae235b
        parser_set_error (error, &ref, NULL,
Packit ae235b
                          G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
Packit ae235b
                          "unterminated string constant");
Packit ae235b
        g_free (token);
Packit ae235b
        g_free (str);
Packit ae235b
        return NULL;
Packit ae235b
Packit ae235b
      case '\\':
Packit ae235b
        switch (token[++i])
Packit ae235b
          {
Packit ae235b
          case '\0':
Packit ae235b
            parser_set_error (error, &ref, NULL,
Packit ae235b
                              G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
Packit ae235b
                              "unterminated string constant");
Packit ae235b
            g_free (token);
Packit ae235b
            g_free (str);
Packit ae235b
            return NULL;
Packit ae235b
Packit ae235b
          case 'u':
Packit ae235b
            if (!unicode_unescape (token, &i, str, &j, 4, &ref, error))
Packit ae235b
              {
Packit ae235b
                g_free (token);
Packit ae235b
                g_free (str);
Packit ae235b
                return NULL;
Packit ae235b
              }
Packit ae235b
            continue;
Packit ae235b
Packit ae235b
          case 'U':
Packit ae235b
            if (!unicode_unescape (token, &i, str, &j, 8, &ref, error))
Packit ae235b
              {
Packit ae235b
                g_free (token);
Packit ae235b
                g_free (str);
Packit ae235b
                return NULL;
Packit ae235b
              }
Packit ae235b
            continue;
Packit ae235b
Packit ae235b
          case 'a': str[j++] = '\a'; i++; continue;
Packit ae235b
          case 'b': str[j++] = '\b'; i++; continue;
Packit ae235b
          case 'f': str[j++] = '\f'; i++; continue;
Packit ae235b
          case 'n': str[j++] = '\n'; i++; continue;
Packit ae235b
          case 'r': str[j++] = '\r'; i++; continue;
Packit ae235b
          case 't': str[j++] = '\t'; i++; continue;
Packit ae235b
          case 'v': str[j++] = '\v'; i++; continue;
Packit ae235b
          case '\n': i++; continue;
Packit ae235b
          }
Packit ae235b
Packit ae235b
      default:
Packit ae235b
        str[j++] = token[i++];
Packit ae235b
      }
Packit ae235b
  str[j++] = '\0';
Packit ae235b
  g_free (token);
Packit ae235b
Packit ae235b
  string = g_slice_new (String);
Packit ae235b
  string->ast.class = &string_class;
Packit ae235b
  string->string = str;
Packit ae235b
Packit ae235b
  token_stream_next (stream);
Packit ae235b
Packit ae235b
  return (AST *) string;
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  AST ast;
Packit ae235b
  gchar *string;
Packit ae235b
} ByteString;
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
bytestring_get_pattern (AST     *ast,
Packit ae235b
                        GError **error)
Packit ae235b
{
Packit ae235b
  return g_strdup ("May");
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
bytestring_get_value (AST                 *ast,
Packit ae235b
                      const GVariantType  *type,
Packit ae235b
                      GError             **error)
Packit ae235b
{
Packit ae235b
  ByteString *string = (ByteString *) ast;
Packit ae235b
Packit ae235b
  if (!g_variant_type_equal (type, G_VARIANT_TYPE_BYTESTRING))
Packit ae235b
    return ast_type_error (ast, type, error);
Packit ae235b
Packit ae235b
  return g_variant_new_bytestring (string->string);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
bytestring_free (AST *ast)
Packit ae235b
{
Packit ae235b
  ByteString *string = (ByteString *) ast;
Packit ae235b
Packit ae235b
  g_free (string->string);
Packit ae235b
  g_slice_free (ByteString, string);
Packit ae235b
}
Packit ae235b
Packit ae235b
static AST *
Packit ae235b
bytestring_parse (TokenStream  *stream,
Packit ae235b
                  va_list      *app,
Packit ae235b
                  GError      **error)
Packit ae235b
{
Packit ae235b
  static const ASTClass bytestring_class = {
Packit ae235b
    bytestring_get_pattern,
Packit ae235b
    maybe_wrapper, bytestring_get_value,
Packit ae235b
    bytestring_free
Packit ae235b
  };
Packit ae235b
  ByteString *string;
Packit ae235b
  SourceRef ref;
Packit ae235b
  gchar *token;
Packit ae235b
  gsize length;
Packit ae235b
  gchar quote;
Packit ae235b
  gchar *str;
Packit ae235b
  gint i, j;
Packit ae235b
Packit ae235b
  token_stream_start_ref (stream, &ref;;
Packit ae235b
  token = token_stream_get (stream);
Packit ae235b
  token_stream_end_ref (stream, &ref;;
Packit ae235b
  g_assert (token[0] == 'b');
Packit ae235b
  length = strlen (token);
Packit ae235b
  quote = token[1];
Packit ae235b
Packit ae235b
  str = g_malloc (length);
Packit ae235b
  g_assert (quote == '"' || quote == '\'');
Packit ae235b
  j = 0;
Packit ae235b
  i = 2;
Packit ae235b
  while (token[i] != quote)
Packit ae235b
    switch (token[i])
Packit ae235b
      {
Packit ae235b
      case '\0':
Packit ae235b
        parser_set_error (error, &ref, NULL,
Packit ae235b
                          G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
Packit ae235b
                          "unterminated string constant");
Packit ae235b
        g_free (str);
Packit ae235b
        g_free (token);
Packit ae235b
        return NULL;
Packit ae235b
Packit ae235b
      case '\\':
Packit ae235b
        switch (token[++i])
Packit ae235b
          {
Packit ae235b
          case '\0':
Packit ae235b
            parser_set_error (error, &ref, NULL,
Packit ae235b
                              G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
Packit ae235b
                              "unterminated string constant");
Packit ae235b
            g_free (str);
Packit ae235b
            g_free (token);
Packit ae235b
            return NULL;
Packit ae235b
Packit ae235b
          case '0': case '1': case '2': case '3':
Packit ae235b
          case '4': case '5': case '6': case '7':
Packit ae235b
            {
Packit ae235b
              /* up to 3 characters */
Packit ae235b
              guchar val = token[i++] - '0';
Packit ae235b
Packit ae235b
              if ('0' <= token[i] && token[i] < '8')
Packit ae235b
                val = (val << 3) | (token[i++] - '0');
Packit ae235b
Packit ae235b
              if ('0' <= token[i] && token[i] < '8')
Packit ae235b
                val = (val << 3) | (token[i++] - '0');
Packit ae235b
Packit ae235b
              str[j++] = val;
Packit ae235b
            }
Packit ae235b
            continue;
Packit ae235b
Packit ae235b
          case 'a': str[j++] = '\a'; i++; continue;
Packit ae235b
          case 'b': str[j++] = '\b'; i++; continue;
Packit ae235b
          case 'f': str[j++] = '\f'; i++; continue;
Packit ae235b
          case 'n': str[j++] = '\n'; i++; continue;
Packit ae235b
          case 'r': str[j++] = '\r'; i++; continue;
Packit ae235b
          case 't': str[j++] = '\t'; i++; continue;
Packit ae235b
          case 'v': str[j++] = '\v'; i++; continue;
Packit ae235b
          case '\n': i++; continue;
Packit ae235b
          }
Packit ae235b
Packit ae235b
      default:
Packit ae235b
        str[j++] = token[i++];
Packit ae235b
      }
Packit ae235b
  str[j++] = '\0';
Packit ae235b
  g_free (token);
Packit ae235b
Packit ae235b
  string = g_slice_new (ByteString);
Packit ae235b
  string->ast.class = &bytestring_class;
Packit ae235b
  string->string = str;
Packit ae235b
Packit ae235b
  token_stream_next (stream);
Packit ae235b
Packit ae235b
  return (AST *) string;
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  AST ast;
Packit ae235b
Packit ae235b
  gchar *token;
Packit ae235b
} Number;
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
number_get_pattern (AST     *ast,
Packit ae235b
                    GError **error)
Packit ae235b
{
Packit ae235b
  Number *number = (Number *) ast;
Packit ae235b
Packit ae235b
  if (strchr (number->token, '.') ||
Packit ae235b
      (!g_str_has_prefix (number->token, "0x") && strchr (number->token, 'e')) ||
Packit ae235b
      strstr (number->token, "inf") ||
Packit ae235b
      strstr (number->token, "nan"))
Packit ae235b
    return g_strdup ("Md");
Packit ae235b
Packit ae235b
  return g_strdup ("MN");
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
number_overflow (AST                 *ast,
Packit ae235b
                 const GVariantType  *type,
Packit ae235b
                 GError             **error)
Packit ae235b
{
Packit ae235b
  ast_set_error (ast, error, NULL,
Packit ae235b
                 G_VARIANT_PARSE_ERROR_NUMBER_OUT_OF_RANGE,
Packit ae235b
                 "number out of range for type '%c'",
Packit ae235b
                 g_variant_type_peek_string (type)[0]);
Packit ae235b
  return NULL;
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
number_get_value (AST                 *ast,
Packit ae235b
                  const GVariantType  *type,
Packit ae235b
                  GError             **error)
Packit ae235b
{
Packit ae235b
  Number *number = (Number *) ast;
Packit ae235b
  const gchar *token;
Packit ae235b
  gboolean negative;
Packit ae235b
  gboolean floating;
Packit ae235b
  guint64 abs_val;
Packit ae235b
  gdouble dbl_val;
Packit ae235b
  gchar *end;
Packit ae235b
Packit ae235b
  token = number->token;
Packit ae235b
Packit ae235b
  if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
Packit ae235b
    {
Packit ae235b
      floating = TRUE;
Packit ae235b
Packit ae235b
      errno = 0;
Packit ae235b
      dbl_val = g_ascii_strtod (token, &end;;
Packit ae235b
      if (dbl_val != 0.0 && errno == ERANGE)
Packit ae235b
        {
Packit ae235b
          ast_set_error (ast, error, NULL,
Packit ae235b
                         G_VARIANT_PARSE_ERROR_NUMBER_TOO_BIG,
Packit ae235b
                         "number too big for any type");
Packit ae235b
          return NULL;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      /* silence uninitialised warnings... */
Packit ae235b
      negative = FALSE;
Packit ae235b
      abs_val = 0;
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      floating = FALSE;
Packit ae235b
      negative = token[0] == '-';
Packit ae235b
      if (token[0] == '-')
Packit ae235b
        token++;
Packit ae235b
Packit ae235b
      errno = 0;
Packit ae235b
      abs_val = g_ascii_strtoull (token, &end, 0);
Packit ae235b
      if (abs_val == G_MAXUINT64 && errno == ERANGE)
Packit ae235b
        {
Packit ae235b
          ast_set_error (ast, error, NULL,
Packit ae235b
                         G_VARIANT_PARSE_ERROR_NUMBER_TOO_BIG,
Packit ae235b
                         "integer too big for any type");
Packit ae235b
          return NULL;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      if (abs_val == 0)
Packit ae235b
        negative = FALSE;
Packit ae235b
Packit ae235b
      /* silence uninitialised warning... */
Packit ae235b
      dbl_val = 0.0;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (*end != '\0')
Packit ae235b
    {
Packit ae235b
      SourceRef ref;
Packit ae235b
Packit ae235b
      ref = ast->source_ref;
Packit ae235b
      ref.start += end - number->token;
Packit ae235b
      ref.end = ref.start + 1;
Packit ae235b
Packit ae235b
      parser_set_error (error, &ref, NULL,
Packit ae235b
                        G_VARIANT_PARSE_ERROR_INVALID_CHARACTER,
Packit ae235b
                        "invalid character in number");
Packit ae235b
      return NULL;
Packit ae235b
     }
Packit ae235b
Packit ae235b
  if (floating)
Packit ae235b
    return g_variant_new_double (dbl_val);
Packit ae235b
Packit ae235b
  switch (*g_variant_type_peek_string (type))
Packit ae235b
    {
Packit ae235b
    case 'y':
Packit ae235b
      if (negative || abs_val > G_MAXUINT8)
Packit ae235b
        return number_overflow (ast, type, error);
Packit ae235b
      return g_variant_new_byte (abs_val);
Packit ae235b
Packit ae235b
    case 'n':
Packit ae235b
      if (abs_val - negative > G_MAXINT16)
Packit ae235b
        return number_overflow (ast, type, error);
Packit ae235b
      return g_variant_new_int16 (negative ? -abs_val : abs_val);
Packit ae235b
Packit ae235b
    case 'q':
Packit ae235b
      if (negative || abs_val > G_MAXUINT16)
Packit ae235b
        return number_overflow (ast, type, error);
Packit ae235b
      return g_variant_new_uint16 (abs_val);
Packit ae235b
Packit ae235b
    case 'i':
Packit ae235b
      if (abs_val - negative > G_MAXINT32)
Packit ae235b
        return number_overflow (ast, type, error);
Packit ae235b
      return g_variant_new_int32 (negative ? -abs_val : abs_val);
Packit ae235b
Packit ae235b
    case 'u':
Packit ae235b
      if (negative || abs_val > G_MAXUINT32)
Packit ae235b
        return number_overflow (ast, type, error);
Packit ae235b
      return g_variant_new_uint32 (abs_val);
Packit ae235b
Packit ae235b
    case 'x':
Packit ae235b
      if (abs_val - negative > G_MAXINT64)
Packit ae235b
        return number_overflow (ast, type, error);
Packit ae235b
      return g_variant_new_int64 (negative ? -abs_val : abs_val);
Packit ae235b
Packit ae235b
    case 't':
Packit ae235b
      if (negative)
Packit ae235b
        return number_overflow (ast, type, error);
Packit ae235b
      return g_variant_new_uint64 (abs_val);
Packit ae235b
Packit ae235b
    case 'h':
Packit ae235b
      if (abs_val - negative > G_MAXINT32)
Packit ae235b
        return number_overflow (ast, type, error);
Packit ae235b
      return g_variant_new_handle (negative ? -abs_val : abs_val);
Packit ae235b
Packit ae235b
    default:
Packit ae235b
      return ast_type_error (ast, type, error);
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
number_free (AST *ast)
Packit ae235b
{
Packit ae235b
  Number *number = (Number *) ast;
Packit ae235b
Packit ae235b
  g_free (number->token);
Packit ae235b
  g_slice_free (Number, number);
Packit ae235b
}
Packit ae235b
Packit ae235b
static AST *
Packit ae235b
number_parse (TokenStream  *stream,
Packit ae235b
              va_list      *app,
Packit ae235b
              GError      **error)
Packit ae235b
{
Packit ae235b
  static const ASTClass number_class = {
Packit ae235b
    number_get_pattern,
Packit ae235b
    maybe_wrapper, number_get_value,
Packit ae235b
    number_free
Packit ae235b
  };
Packit ae235b
  Number *number;
Packit ae235b
Packit ae235b
  number = g_slice_new (Number);
Packit ae235b
  number->ast.class = &number_class;
Packit ae235b
  number->token = token_stream_get (stream);
Packit ae235b
  token_stream_next (stream);
Packit ae235b
Packit ae235b
  return (AST *) number;
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  AST ast;
Packit ae235b
  gboolean value;
Packit ae235b
} Boolean;
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
boolean_get_pattern (AST     *ast,
Packit ae235b
                     GError **error)
Packit ae235b
{
Packit ae235b
  return g_strdup ("Mb");
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
boolean_get_value (AST                 *ast,
Packit ae235b
                   const GVariantType  *type,
Packit ae235b
                   GError             **error)
Packit ae235b
{
Packit ae235b
  Boolean *boolean = (Boolean *) ast;
Packit ae235b
Packit ae235b
  if (!g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
Packit ae235b
    return ast_type_error (ast, type, error);
Packit ae235b
Packit ae235b
  return g_variant_new_boolean (boolean->value);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
boolean_free (AST *ast)
Packit ae235b
{
Packit ae235b
  Boolean *boolean = (Boolean *) ast;
Packit ae235b
Packit ae235b
  g_slice_free (Boolean, boolean);
Packit ae235b
}
Packit ae235b
Packit ae235b
static AST *
Packit ae235b
boolean_new (gboolean value)
Packit ae235b
{
Packit ae235b
  static const ASTClass boolean_class = {
Packit ae235b
    boolean_get_pattern,
Packit ae235b
    maybe_wrapper, boolean_get_value,
Packit ae235b
    boolean_free
Packit ae235b
  };
Packit ae235b
  Boolean *boolean;
Packit ae235b
Packit ae235b
  boolean = g_slice_new (Boolean);
Packit ae235b
  boolean->ast.class = &boolean_class;
Packit ae235b
  boolean->value = value;
Packit ae235b
Packit ae235b
  return (AST *) boolean;
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  AST ast;
Packit ae235b
Packit ae235b
  GVariant *value;
Packit ae235b
} Positional;
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
positional_get_pattern (AST     *ast,
Packit ae235b
                        GError **error)
Packit ae235b
{
Packit ae235b
  Positional *positional = (Positional *) ast;
Packit ae235b
Packit ae235b
  return g_strdup (g_variant_get_type_string (positional->value));
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
positional_get_value (AST                 *ast,
Packit ae235b
                      const GVariantType  *type,
Packit ae235b
                      GError             **error)
Packit ae235b
{
Packit ae235b
  Positional *positional = (Positional *) ast;
Packit ae235b
  GVariant *value;
Packit ae235b
Packit ae235b
  g_assert (positional->value != NULL);
Packit ae235b
Packit ae235b
  if G_UNLIKELY (!g_variant_is_of_type (positional->value, type))
Packit ae235b
    return ast_type_error (ast, type, error);
Packit ae235b
Packit ae235b
  /* NOTE: if _get is called more than once then
Packit ae235b
   * things get messed up with respect to floating refs.
Packit ae235b
   *
Packit ae235b
   * fortunately, this function should only ever get called once.
Packit ae235b
   */
Packit ae235b
  g_assert (positional->value != NULL);
Packit ae235b
  value = positional->value;
Packit ae235b
  positional->value = NULL;
Packit ae235b
Packit ae235b
  return value;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
positional_free (AST *ast)
Packit ae235b
{
Packit ae235b
  Positional *positional = (Positional *) ast;
Packit ae235b
Packit ae235b
  /* if positional->value is set, just leave it.
Packit ae235b
   * memory management doesn't matter in case of programmer error.
Packit ae235b
   */
Packit ae235b
  g_slice_free (Positional, positional);
Packit ae235b
}
Packit ae235b
Packit ae235b
static AST *
Packit ae235b
positional_parse (TokenStream  *stream,
Packit ae235b
                  va_list      *app,
Packit ae235b
                  GError      **error)
Packit ae235b
{
Packit ae235b
  static const ASTClass positional_class = {
Packit ae235b
    positional_get_pattern,
Packit ae235b
    positional_get_value, NULL,
Packit ae235b
    positional_free
Packit ae235b
  };
Packit ae235b
  Positional *positional;
Packit ae235b
  const gchar *endptr;
Packit ae235b
  gchar *token;
Packit ae235b
Packit ae235b
  token = token_stream_get (stream);
Packit ae235b
  g_assert (token[0] == '%');
Packit ae235b
Packit ae235b
  positional = g_slice_new (Positional);
Packit ae235b
  positional->ast.class = &positional_class;
Packit ae235b
  positional->value = g_variant_new_va (token + 1, &endptr, app);
Packit ae235b
Packit ae235b
  if (*endptr || positional->value == NULL)
Packit ae235b
    {
Packit ae235b
      token_stream_set_error (stream, error, TRUE,
Packit ae235b
                              G_VARIANT_PARSE_ERROR_INVALID_FORMAT_STRING,
Packit ae235b
                              "invalid GVariant format string");
Packit ae235b
      /* memory management doesn't matter in case of programmer error. */
Packit ae235b
      return NULL;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  token_stream_next (stream);
Packit ae235b
  g_free (token);
Packit ae235b
Packit ae235b
  return (AST *) positional;
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  AST ast;
Packit ae235b
Packit ae235b
  GVariantType *type;
Packit ae235b
  AST *child;
Packit ae235b
} TypeDecl;
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
typedecl_get_pattern (AST     *ast,
Packit ae235b
                      GError **error)
Packit ae235b
{
Packit ae235b
  TypeDecl *decl = (TypeDecl *) ast;
Packit ae235b
Packit ae235b
  return g_variant_type_dup_string (decl->type);
Packit ae235b
}
Packit ae235b
Packit ae235b
static GVariant *
Packit ae235b
typedecl_get_value (AST                 *ast,
Packit ae235b
                    const GVariantType  *type,
Packit ae235b
                    GError             **error)
Packit ae235b
{
Packit ae235b
  TypeDecl *decl = (TypeDecl *) ast;
Packit ae235b
Packit ae235b
  return ast_get_value (decl->child, type, error);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
typedecl_free (AST *ast)
Packit ae235b
{
Packit ae235b
  TypeDecl *decl = (TypeDecl *) ast;
Packit ae235b
Packit ae235b
  ast_free (decl->child);
Packit ae235b
  g_variant_type_free (decl->type);
Packit ae235b
  g_slice_free (TypeDecl, decl);
Packit ae235b
}
Packit ae235b
Packit ae235b
static AST *
Packit ae235b
typedecl_parse (TokenStream  *stream,
Packit ae235b
                va_list      *app,
Packit ae235b
                GError      **error)
Packit ae235b
{
Packit ae235b
  static const ASTClass typedecl_class = {
Packit ae235b
    typedecl_get_pattern,
Packit ae235b
    typedecl_get_value, NULL,
Packit ae235b
    typedecl_free
Packit ae235b
  };
Packit ae235b
  GVariantType *type;
Packit ae235b
  TypeDecl *decl;
Packit ae235b
  AST *child;
Packit ae235b
Packit ae235b
  if (token_stream_peek (stream, '@'))
Packit ae235b
    {
Packit ae235b
      gchar *token;
Packit ae235b
Packit ae235b
      token = token_stream_get (stream);
Packit ae235b
Packit ae235b
      if (!g_variant_type_string_is_valid (token + 1))
Packit ae235b
        {
Packit ae235b
          token_stream_set_error (stream, error, TRUE,
Packit ae235b
                                  G_VARIANT_PARSE_ERROR_INVALID_TYPE_STRING,
Packit ae235b
                                  "invalid type declaration");
Packit ae235b
          g_free (token);
Packit ae235b
Packit ae235b
          return NULL;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      type = g_variant_type_new (token + 1);
Packit ae235b
Packit ae235b
      if (!g_variant_type_is_definite (type))
Packit ae235b
        {
Packit ae235b
          token_stream_set_error (stream, error, TRUE,
Packit ae235b
                                  G_VARIANT_PARSE_ERROR_DEFINITE_TYPE_EXPECTED,
Packit ae235b
                                  "type declarations must be definite");
Packit ae235b
          g_variant_type_free (type);
Packit ae235b
          g_free (token);
Packit ae235b
Packit ae235b
          return NULL;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      token_stream_next (stream);
Packit ae235b
      g_free (token);
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      if (token_stream_consume (stream, "boolean"))
Packit ae235b
        type = g_variant_type_copy (G_VARIANT_TYPE_BOOLEAN);
Packit ae235b
Packit ae235b
      else if (token_stream_consume (stream, "byte"))
Packit ae235b
        type = g_variant_type_copy (G_VARIANT_TYPE_BYTE);
Packit ae235b
Packit ae235b
      else if (token_stream_consume (stream, "int16"))
Packit ae235b
        type = g_variant_type_copy (G_VARIANT_TYPE_INT16);
Packit ae235b
Packit ae235b
      else if (token_stream_consume (stream, "uint16"))
Packit ae235b
        type = g_variant_type_copy (G_VARIANT_TYPE_UINT16);
Packit ae235b
Packit ae235b
      else if (token_stream_consume (stream, "int32"))
Packit ae235b
        type = g_variant_type_copy (G_VARIANT_TYPE_INT32);
Packit ae235b
Packit ae235b
      else if (token_stream_consume (stream, "handle"))
Packit ae235b
        type = g_variant_type_copy (G_VARIANT_TYPE_HANDLE);
Packit ae235b
Packit ae235b
      else if (token_stream_consume (stream, "uint32"))
Packit ae235b
        type = g_variant_type_copy (G_VARIANT_TYPE_UINT32);
Packit ae235b
Packit ae235b
      else if (token_stream_consume (stream, "int64"))
Packit ae235b
        type = g_variant_type_copy (G_VARIANT_TYPE_INT64);
Packit ae235b
Packit ae235b
      else if (token_stream_consume (stream, "uint64"))
Packit ae235b
        type = g_variant_type_copy (G_VARIANT_TYPE_UINT64);
Packit ae235b
Packit ae235b
      else if (token_stream_consume (stream, "double"))
Packit ae235b
        type = g_variant_type_copy (G_VARIANT_TYPE_DOUBLE);
Packit ae235b
Packit ae235b
      else if (token_stream_consume (stream, "string"))
Packit ae235b
        type = g_variant_type_copy (G_VARIANT_TYPE_STRING);
Packit ae235b
Packit ae235b
      else if (token_stream_consume (stream, "objectpath"))
Packit ae235b
        type = g_variant_type_copy (G_VARIANT_TYPE_OBJECT_PATH);
Packit ae235b
Packit ae235b
      else if (token_stream_consume (stream, "signature"))
Packit ae235b
        type = g_variant_type_copy (G_VARIANT_TYPE_SIGNATURE);
Packit ae235b
Packit ae235b
      else
Packit ae235b
        {
Packit ae235b
          token_stream_set_error (stream, error, TRUE,
Packit ae235b
                                  G_VARIANT_PARSE_ERROR_UNKNOWN_KEYWORD,
Packit ae235b
                                  "unknown keyword");
Packit ae235b
          return NULL;
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if ((child = parse (stream, app, error)) == NULL)
Packit ae235b
    {
Packit ae235b
      g_variant_type_free (type);
Packit ae235b
      return NULL;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  decl = g_slice_new (TypeDecl);
Packit ae235b
  decl->ast.class = &typedecl_class;
Packit ae235b
  decl->type = type;
Packit ae235b
  decl->child = child;
Packit ae235b
Packit ae235b
  return (AST *) decl;
Packit ae235b
}
Packit ae235b
Packit ae235b
static AST *
Packit ae235b
parse (TokenStream  *stream,
Packit ae235b
       va_list      *app,
Packit ae235b
       GError      **error)
Packit ae235b
{
Packit ae235b
  SourceRef source_ref;
Packit ae235b
  AST *result;
Packit ae235b
Packit ae235b
  token_stream_prepare (stream);
Packit ae235b
  token_stream_start_ref (stream, &source_ref);
Packit ae235b
Packit ae235b
  if (token_stream_peek (stream, '['))
Packit ae235b
    result = array_parse (stream, app, error);
Packit ae235b
Packit ae235b
  else if (token_stream_peek (stream, '('))
Packit ae235b
    result = tuple_parse (stream, app, error);
Packit ae235b
Packit ae235b
  else if (token_stream_peek (stream, '<'))
Packit ae235b
    result = variant_parse (stream, app, error);
Packit ae235b
Packit ae235b
  else if (token_stream_peek (stream, '{'))
Packit ae235b
    result = dictionary_parse (stream, app, error);
Packit ae235b
Packit ae235b
  else if (app && token_stream_peek (stream, '%'))
Packit ae235b
    result = positional_parse (stream, app, error);
Packit ae235b
Packit ae235b
  else if (token_stream_consume (stream, "true"))
Packit ae235b
    result = boolean_new (TRUE);
Packit ae235b
Packit ae235b
  else if (token_stream_consume (stream, "false"))
Packit ae235b
    result = boolean_new (FALSE);
Packit ae235b
Packit ae235b
  else if (token_stream_is_numeric (stream) ||
Packit ae235b
           token_stream_peek_string (stream, "inf") ||
Packit ae235b
           token_stream_peek_string (stream, "nan"))
Packit ae235b
    result = number_parse (stream, app, error);
Packit ae235b
Packit ae235b
  else if (token_stream_peek (stream, 'n') ||
Packit ae235b
           token_stream_peek (stream, 'j'))
Packit ae235b
    result = maybe_parse (stream, app, error);
Packit ae235b
Packit ae235b
  else if (token_stream_peek (stream, '@') ||
Packit ae235b
           token_stream_is_keyword (stream))
Packit ae235b
    result = typedecl_parse (stream, app, error);
Packit ae235b
Packit ae235b
  else if (token_stream_peek (stream, '\'') ||
Packit ae235b
           token_stream_peek (stream, '"'))
Packit ae235b
    result = string_parse (stream, app, error);
Packit ae235b
Packit ae235b
  else if (token_stream_peek2 (stream, 'b', '\'') ||
Packit ae235b
           token_stream_peek2 (stream, 'b', '"'))
Packit ae235b
    result = bytestring_parse (stream, app, error);
Packit ae235b
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      token_stream_set_error (stream, error, FALSE,
Packit ae235b
                              G_VARIANT_PARSE_ERROR_VALUE_EXPECTED,
Packit ae235b
                              "expected value");
Packit ae235b
      return NULL;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (result != NULL)
Packit ae235b
    {
Packit ae235b
      token_stream_end_ref (stream, &source_ref);
Packit ae235b
      result->source_ref = source_ref;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return result;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_variant_parse:
Packit ae235b
 * @type: (nullable): a #GVariantType, or %NULL
Packit ae235b
 * @text: a string containing a GVariant in text form
Packit ae235b
 * @limit: (nullable): a pointer to the end of @text, or %NULL
Packit ae235b
 * @endptr: (nullable): a location to store the end pointer, or %NULL
Packit ae235b
 * @error: (nullable): a pointer to a %NULL #GError pointer, or %NULL
Packit ae235b
 *
Packit ae235b
 * Parses a #GVariant from a text representation.
Packit ae235b
 *
Packit ae235b
 * A single #GVariant is parsed from the content of @text.
Packit ae235b
 *
Packit ae235b
 * The format is described [here][gvariant-text].
Packit ae235b
 *
Packit ae235b
 * The memory at @limit will never be accessed and the parser behaves as
Packit ae235b
 * if the character at @limit is the nul terminator.  This has the
Packit ae235b
 * effect of bounding @text.
Packit ae235b
 *
Packit ae235b
 * If @endptr is non-%NULL then @text is permitted to contain data
Packit ae235b
 * following the value that this function parses and @endptr will be
Packit ae235b
 * updated to point to the first character past the end of the text
Packit ae235b
 * parsed by this function.  If @endptr is %NULL and there is extra data
Packit ae235b
 * then an error is returned.
Packit ae235b
 *
Packit ae235b
 * If @type is non-%NULL then the value will be parsed to have that
Packit ae235b
 * type.  This may result in additional parse errors (in the case that
Packit ae235b
 * the parsed value doesn't fit the type) but may also result in fewer
Packit ae235b
 * errors (in the case that the type would have been ambiguous, such as
Packit ae235b
 * with empty arrays).
Packit ae235b
 *
Packit ae235b
 * In the event that the parsing is successful, the resulting #GVariant
Packit ae235b
 * is returned. It is never floating, and must be freed with
Packit ae235b
 * g_variant_unref().
Packit ae235b
 *
Packit ae235b
 * In case of any error, %NULL will be returned.  If @error is non-%NULL
Packit ae235b
 * then it will be set to reflect the error that occurred.
Packit ae235b
 *
Packit ae235b
 * Officially, the language understood by the parser is "any string
Packit ae235b
 * produced by g_variant_print()".
Packit ae235b
 *
Packit ae235b
 * Returns: a non-floating reference to a #GVariant, or %NULL
Packit ae235b
 **/
Packit ae235b
GVariant *
Packit ae235b
g_variant_parse (const GVariantType  *type,
Packit ae235b
                 const gchar         *text,
Packit ae235b
                 const gchar         *limit,
Packit ae235b
                 const gchar        **endptr,
Packit ae235b
                 GError             **error)
Packit ae235b
{
Packit ae235b
  TokenStream stream = { 0, };
Packit ae235b
  GVariant *result = NULL;
Packit ae235b
  AST *ast;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (text != NULL, NULL);
Packit ae235b
  g_return_val_if_fail (text == limit || text != NULL, NULL);
Packit ae235b
Packit ae235b
  stream.start = text;
Packit ae235b
  stream.stream = text;
Packit ae235b
  stream.end = limit;
Packit ae235b
Packit ae235b
  if ((ast = parse (&stream, NULL, error)))
Packit ae235b
    {
Packit ae235b
      if (type == NULL)
Packit ae235b
        result = ast_resolve (ast, error);
Packit ae235b
      else
Packit ae235b
        result = ast_get_value (ast, type, error);
Packit ae235b
Packit ae235b
      if (result != NULL)
Packit ae235b
        {
Packit ae235b
          g_variant_ref_sink (result);
Packit ae235b
Packit ae235b
          if (endptr == NULL)
Packit ae235b
            {
Packit ae235b
              while (stream.stream != limit &&
Packit ae235b
                     g_ascii_isspace (*stream.stream))
Packit ae235b
                stream.stream++;
Packit ae235b
Packit ae235b
              if (stream.stream != limit && *stream.stream != '\0')
Packit ae235b
                {
Packit ae235b
                  SourceRef ref = { stream.stream - text,
Packit ae235b
                                    stream.stream - text };
Packit ae235b
Packit ae235b
                  parser_set_error (error, &ref, NULL,
Packit ae235b
                                    G_VARIANT_PARSE_ERROR_INPUT_NOT_AT_END,
Packit ae235b
                                    "expected end of input");
Packit ae235b
                  g_variant_unref (result);
Packit ae235b
Packit ae235b
                  result = NULL;
Packit ae235b
                }
Packit ae235b
            }
Packit ae235b
          else
Packit ae235b
            *endptr = stream.stream;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      ast_free (ast);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return result;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_variant_new_parsed_va:
Packit ae235b
 * @format: a text format #GVariant
Packit ae235b
 * @app: a pointer to a #va_list
Packit ae235b
 *
Packit ae235b
 * Parses @format and returns the result.
Packit ae235b
 *
Packit ae235b
 * This is the version of g_variant_new_parsed() intended to be used
Packit ae235b
 * from libraries.
Packit ae235b
 *
Packit ae235b
 * The return value will be floating if it was a newly created GVariant
Packit ae235b
 * instance.  In the case that @format simply specified the collection
Packit ae235b
 * of a #GVariant pointer (eg: @format was "%*") then the collected
Packit ae235b
 * #GVariant pointer will be returned unmodified, without adding any
Packit ae235b
 * additional references.
Packit ae235b
 *
Packit ae235b
 * Note that the arguments in @app must be of the correct width for their types
Packit ae235b
 * specified in @format when collected into the #va_list. See
Packit ae235b
 * the [GVariant varargs documentation][gvariant-varargs].
Packit ae235b
 *
Packit ae235b
 * In order to behave correctly in all cases it is necessary for the
Packit ae235b
 * calling function to g_variant_ref_sink() the return result before
Packit ae235b
 * returning control to the user that originally provided the pointer.
Packit ae235b
 * At this point, the caller will have their own full reference to the
Packit ae235b
 * result.  This can also be done by adding the result to a container,
Packit ae235b
 * or by passing it to another g_variant_new() call.
Packit ae235b
 *
Packit ae235b
 * Returns: a new, usually floating, #GVariant
Packit ae235b
 **/
Packit ae235b
GVariant *
Packit ae235b
g_variant_new_parsed_va (const gchar *format,
Packit ae235b
                         va_list     *app)
Packit ae235b
{
Packit ae235b
  TokenStream stream = { 0, };
Packit ae235b
  GVariant *result = NULL;
Packit ae235b
  GError *error = NULL;
Packit ae235b
  AST *ast;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (format != NULL, NULL);
Packit ae235b
  g_return_val_if_fail (app != NULL, NULL);
Packit ae235b
Packit ae235b
  stream.start = format;
Packit ae235b
  stream.stream = format;
Packit ae235b
  stream.end = NULL;
Packit ae235b
Packit ae235b
  if ((ast = parse (&stream, app, &error)))
Packit ae235b
    {
Packit ae235b
      result = ast_resolve (ast, &error);
Packit ae235b
      ast_free (ast);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (result == NULL)
Packit ae235b
    g_error ("g_variant_new_parsed: %s", error->message);
Packit ae235b
Packit ae235b
  if (*stream.stream)
Packit ae235b
    g_error ("g_variant_new_parsed: trailing text after value");
Packit ae235b
Packit ae235b
  return result;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_variant_new_parsed:
Packit ae235b
 * @format: a text format #GVariant
Packit ae235b
 * @...: arguments as per @format
Packit ae235b
 *
Packit ae235b
 * Parses @format and returns the result.
Packit ae235b
 *
Packit ae235b
 * @format must be a text format #GVariant with one extension: at any
Packit ae235b
 * point that a value may appear in the text, a '%' character followed
Packit ae235b
 * by a GVariant format string (as per g_variant_new()) may appear.  In
Packit ae235b
 * that case, the same arguments are collected from the argument list as
Packit ae235b
 * g_variant_new() would have collected.
Packit ae235b
 *
Packit ae235b
 * Note that the arguments must be of the correct width for their types
Packit ae235b
 * specified in @format. This can be achieved by casting them. See
Packit ae235b
 * the [GVariant varargs documentation][gvariant-varargs].
Packit ae235b
 *
Packit ae235b
 * Consider this simple example:
Packit ae235b
 * |[ 
Packit ae235b
 *  g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
Packit ae235b
 * ]|
Packit ae235b
 *
Packit ae235b
 * In the example, the variable argument parameters are collected and
Packit ae235b
 * filled in as if they were part of the original string to produce the
Packit ae235b
 * result of
Packit ae235b
 * |[ 
Packit ae235b
 * [('one', 1), ('two', 2), ('three', 3)]
Packit ae235b
 * ]|
Packit ae235b
 *
Packit ae235b
 * This function is intended only to be used with @format as a string
Packit ae235b
 * literal.  Any parse error is fatal to the calling process.  If you
Packit ae235b
 * want to parse data from untrusted sources, use g_variant_parse().
Packit ae235b
 *
Packit ae235b
 * You may not use this function to return, unmodified, a single
Packit ae235b
 * #GVariant pointer from the argument list.  ie: @format may not solely
Packit ae235b
 * be anything along the lines of "%*", "%?", "\%r", or anything starting
Packit ae235b
 * with "%@".
Packit ae235b
 *
Packit ae235b
 * Returns: a new floating #GVariant instance
Packit ae235b
 **/
Packit ae235b
GVariant *
Packit ae235b
g_variant_new_parsed (const gchar *format,
Packit ae235b
                      ...)
Packit ae235b
{
Packit ae235b
  GVariant *result;
Packit ae235b
  va_list ap;
Packit ae235b
Packit ae235b
  va_start (ap, format);
Packit ae235b
  result = g_variant_new_parsed_va (format, &ap);
Packit ae235b
  va_end (ap);
Packit ae235b
Packit ae235b
  return result;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_variant_builder_add_parsed:
Packit ae235b
 * @builder: a #GVariantBuilder
Packit ae235b
 * @format: a text format #GVariant
Packit ae235b
 * @...: arguments as per @format
Packit ae235b
 *
Packit ae235b
 * Adds to a #GVariantBuilder.
Packit ae235b
 *
Packit ae235b
 * This call is a convenience wrapper that is exactly equivalent to
Packit ae235b
 * calling g_variant_new_parsed() followed by
Packit ae235b
 * g_variant_builder_add_value().
Packit ae235b
 *
Packit ae235b
 * Note that the arguments must be of the correct width for their types
Packit ae235b
 * specified in @format_string. This can be achieved by casting them. See
Packit ae235b
 * the [GVariant varargs documentation][gvariant-varargs].
Packit ae235b
 *
Packit ae235b
 * This function might be used as follows:
Packit ae235b
 *
Packit ae235b
 * |[ 
Packit ae235b
 * GVariant *
Packit ae235b
 * make_pointless_dictionary (void)
Packit ae235b
 * {
Packit ae235b
 *   GVariantBuilder builder;
Packit ae235b
 *   int i;
Packit ae235b
 *
Packit ae235b
 *   g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
Packit ae235b
 *   g_variant_builder_add_parsed (&builder, "{'width', <%i>}", 600);
Packit ae235b
 *   g_variant_builder_add_parsed (&builder, "{'title', <%s>}", "foo");
Packit ae235b
 *   g_variant_builder_add_parsed (&builder, "{'transparency', <0.5>}");
Packit ae235b
 *   return g_variant_builder_end (&builder);
Packit ae235b
 * }
Packit ae235b
 * ]|
Packit ae235b
 *
Packit ae235b
 * Since: 2.26
Packit ae235b
 */
Packit ae235b
void
Packit ae235b
g_variant_builder_add_parsed (GVariantBuilder *builder,
Packit ae235b
                              const gchar     *format,
Packit ae235b
                              ...)
Packit ae235b
{
Packit ae235b
  va_list ap;
Packit ae235b
Packit ae235b
  va_start (ap, format);
Packit ae235b
  g_variant_builder_add_value (builder, g_variant_new_parsed_va (format, &ap);;
Packit ae235b
  va_end (ap);
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
parse_num (const gchar *num,
Packit ae235b
           const gchar *limit,
Packit ae235b
           gint        *result)
Packit ae235b
{
Packit ae235b
  gchar *endptr;
Packit ae235b
  gint64 bignum;
Packit ae235b
Packit ae235b
  bignum = g_ascii_strtoll (num, &endptr, 10);
Packit ae235b
Packit ae235b
  if (endptr != limit)
Packit ae235b
    return FALSE;
Packit ae235b
Packit ae235b
  if (bignum < 0 || bignum > G_MAXINT)
Packit ae235b
    return FALSE;
Packit ae235b
Packit ae235b
  *result = bignum;
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
add_last_line (GString     *err,
Packit ae235b
               const gchar *str)
Packit ae235b
{
Packit ae235b
  const gchar *last_nl;
Packit ae235b
  gchar *chomped;
Packit ae235b
  gint i;
Packit ae235b
Packit ae235b
  /* This is an error at the end of input.  If we have a file
Packit ae235b
   * with newlines, that's probably the empty string after the
Packit ae235b
   * last newline, which is not the most useful thing to show.
Packit ae235b
   *
Packit ae235b
   * Instead, show the last line of non-whitespace that we have
Packit ae235b
   * and put the pointer at the end of it.
Packit ae235b
   */
Packit ae235b
  chomped = g_strchomp (g_strdup (str));
Packit ae235b
  last_nl = strrchr (chomped, '\n');
Packit ae235b
  if (last_nl == NULL)
Packit ae235b
    last_nl = chomped;
Packit ae235b
  else
Packit ae235b
    last_nl++;
Packit ae235b
Packit ae235b
  /* Print the last line like so:
Packit ae235b
   *
Packit ae235b
   *   [1, 2, 3,
Packit ae235b
   *            ^
Packit ae235b
   */
Packit ae235b
  g_string_append (err, "  ");
Packit ae235b
  if (last_nl[0])
Packit ae235b
    g_string_append (err, last_nl);
Packit ae235b
  else
Packit ae235b
    g_string_append (err, "(empty input)");
Packit ae235b
  g_string_append (err, "\n  ");
Packit ae235b
  for (i = 0; last_nl[i]; i++)
Packit ae235b
    g_string_append_c (err, ' ');
Packit ae235b
  g_string_append (err, "^\n");
Packit ae235b
  g_free (chomped);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
add_lines_from_range (GString     *err,
Packit ae235b
                      const gchar *str,
Packit ae235b
                      const gchar *start1,
Packit ae235b
                      const gchar *end1,
Packit ae235b
                      const gchar *start2,
Packit ae235b
                      const gchar *end2)
Packit ae235b
{
Packit ae235b
  while (str < end1 || str < end2)
Packit ae235b
    {
Packit ae235b
      const gchar *nl;
Packit ae235b
Packit ae235b
      nl = str + strcspn (str, "\n");
Packit ae235b
Packit ae235b
      if ((start1 < nl && str < end1) || (start2 < nl && str < end2))
Packit ae235b
        {
Packit ae235b
          const gchar *s;
Packit ae235b
Packit ae235b
          /* We're going to print this line */
Packit ae235b
          g_string_append (err, "  ");
Packit ae235b
          g_string_append_len (err, str, nl - str);
Packit ae235b
          g_string_append (err, "\n  ");
Packit ae235b
Packit ae235b
          /* And add underlines... */
Packit ae235b
          for (s = str; s < nl; s++)
Packit ae235b
            {
Packit ae235b
              if ((start1 <= s && s < end1) || (start2 <= s && s < end2))
Packit ae235b
                g_string_append_c (err, '^');
Packit ae235b
              else
Packit ae235b
                g_string_append_c (err, ' ');
Packit ae235b
            }
Packit ae235b
          g_string_append_c (err, '\n');
Packit ae235b
        }
Packit ae235b
Packit ae235b
      if (!*nl)
Packit ae235b
        break;
Packit ae235b
Packit ae235b
      str = nl + 1;
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_variant_parse_error_print_context:
Packit ae235b
 * @error: a #GError from the #GVariantParseError domain
Packit ae235b
 * @source_str: the string that was given to the parser
Packit ae235b
 *
Packit ae235b
 * Pretty-prints a message showing the context of a #GVariant parse
Packit ae235b
 * error within the string for which parsing was attempted.
Packit ae235b
 *
Packit ae235b
 * The resulting string is suitable for output to the console or other
Packit ae235b
 * monospace media where newlines are treated in the usual way.
Packit ae235b
 *
Packit ae235b
 * The message will typically look something like one of the following:
Packit ae235b
 *
Packit ae235b
 * |[
Packit ae235b
 * unterminated string constant:
Packit ae235b
 *   (1, 2, 3, 'abc
Packit ae235b
 *             ^^^^
Packit ae235b
 * ]|
Packit ae235b
 *
Packit ae235b
 * or
Packit ae235b
 *
Packit ae235b
 * |[
Packit ae235b
 * unable to find a common type:
Packit ae235b
 *   [1, 2, 3, 'str']
Packit ae235b
 *    ^        ^^^^^
Packit ae235b
 * ]|
Packit ae235b
 *
Packit ae235b
 * The format of the message may change in a future version.
Packit ae235b
 *
Packit ae235b
 * @error must have come from a failed attempt to g_variant_parse() and
Packit ae235b
 * @source_str must be exactly the same string that caused the error.
Packit ae235b
 * If @source_str was not nul-terminated when you passed it to
Packit ae235b
 * g_variant_parse() then you must add nul termination before using this
Packit ae235b
 * function.
Packit ae235b
 *
Packit ae235b
 * Returns: (transfer full): the printed message
Packit ae235b
 *
Packit ae235b
 * Since: 2.40
Packit ae235b
 **/
Packit ae235b
gchar *
Packit ae235b
g_variant_parse_error_print_context (GError      *error,
Packit ae235b
                                     const gchar *source_str)
Packit ae235b
{
Packit ae235b
  const gchar *colon, *dash, *comma;
Packit ae235b
  gboolean success = FALSE;
Packit ae235b
  GString *err;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (error->domain == G_VARIANT_PARSE_ERROR, FALSE);
Packit ae235b
Packit ae235b
  /* We can only have a limited number of possible types of ranges
Packit ae235b
   * emitted from the parser:
Packit ae235b
   *
Packit ae235b
   *  - a:          -- usually errors from the tokeniser (eof, invalid char, etc.)
Packit ae235b
   *  - a-b:        -- usually errors from handling one single token
Packit ae235b
   *  - a-b,c-d:    -- errors involving two tokens (ie: type inferencing)
Packit ae235b
   *
Packit ae235b
   * We never see, for example "a,c".
Packit ae235b
   */
Packit ae235b
Packit ae235b
  colon = strchr (error->message, ':');
Packit ae235b
  dash = strchr (error->message, '-');
Packit ae235b
  comma = strchr (error->message, ',');
Packit ae235b
Packit ae235b
  if (!colon)
Packit ae235b
    return NULL;
Packit ae235b
Packit ae235b
  err = g_string_new (colon + 1);
Packit ae235b
  g_string_append (err, ":\n");
Packit ae235b
Packit ae235b
  if (dash == NULL || colon < dash)
Packit ae235b
    {
Packit ae235b
      gint point;
Packit ae235b
Packit ae235b
      /* we have a single point */
Packit ae235b
      if (!parse_num (error->message, colon, &point))
Packit ae235b
        goto out;
Packit ae235b
Packit ae235b
      if (point >= strlen (source_str))
Packit ae235b
        /* the error is at the end of the input */
Packit ae235b
        add_last_line (err, source_str);
Packit ae235b
      else
Packit ae235b
        /* otherwise just treat it as a error at a thin range */
Packit ae235b
        add_lines_from_range (err, source_str, source_str + point, source_str + point + 1, NULL, NULL);
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      /* We have one or two ranges... */
Packit ae235b
      if (comma && comma < colon)
Packit ae235b
        {
Packit ae235b
          gint start1, end1, start2, end2;
Packit ae235b
          const gchar *dash2;
Packit ae235b
Packit ae235b
          /* Two ranges */
Packit ae235b
          dash2 = strchr (comma, '-');
Packit ae235b
Packit ae235b
          if (!parse_num (error->message, dash, &start1) || !parse_num (dash + 1, comma, &end1) ||
Packit ae235b
              !parse_num (comma + 1, dash2, &start2) || !parse_num (dash2 + 1, colon, &end2))
Packit ae235b
            goto out;
Packit ae235b
Packit ae235b
          add_lines_from_range (err, source_str,
Packit ae235b
                                source_str + start1, source_str + end1,
Packit ae235b
                                source_str + start2, source_str + end2);
Packit ae235b
        }
Packit ae235b
      else
Packit ae235b
        {
Packit ae235b
          gint start, end;
Packit ae235b
Packit ae235b
          /* One range */
Packit ae235b
          if (!parse_num (error->message, dash, &start) || !parse_num (dash + 1, colon, &end))
Packit ae235b
            goto out;
Packit ae235b
Packit ae235b
          add_lines_from_range (err, source_str, source_str + start, source_str + end, NULL, NULL);
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
Packit ae235b
  success = TRUE;
Packit ae235b
Packit ae235b
out:
Packit ae235b
  return g_string_free (err, !success);
Packit ae235b
}