2 * Copyright © 2009, 2010 Codethink Limited
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the licence, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
19 * Author: Ryan Lortie <desrt@desrt.ca>
31 #include "gstrfuncs.h"
32 #include "gtestutils.h"
34 #include "gvarianttype.h"
39 * designed by ryan lortie and william hua
40 * designed in itb-229 and at ghazi's, 2009.
44 * G_VARIANT_PARSE_ERROR:
46 * Error domain for GVariant text format parsing. Specific error codes
47 * are not currently defined for this domain. See #GError for
48 * information on error domains.
52 * @G_VARIANT_PARSE_ERROR_FAILED: generic error (unused)
53 * @G_VARIANT_PARSE_ERROR_BASIC_TYPE_EXPECTED: a non-basic #GVariantType was given where a basic type was expected
54 * @G_VARIANT_PARSE_ERROR_CANNOT_INFER_TYPE: cannot infer the #GVariantType
55 * @G_VARIANT_PARSE_ERROR_DEFINITE_TYPE_EXPECTED: an indefinite #GVariantType was given where a definite type was expected
56 * @G_VARIANT_PARSE_ERROR_INPUT_NOT_AT_END: extra data after parsing finished
57 * @G_VARIANT_PARSE_ERROR_INVALID_CHARACTER: invalid character in number or unicode escape
58 * @G_VARIANT_PARSE_ERROR_INVALID_FORMAT_STRING: not a valid #GVariant format string
59 * @G_VARIANT_PARSE_ERROR_INVALID_OBJECT_PATH: not a valid object path
60 * @G_VARIANT_PARSE_ERROR_INVALID_SIGNATURE: not a valid type signature
61 * @G_VARIANT_PARSE_ERROR_INVALID_TYPE_STRING: not a valid #GVariant type string
62 * @G_VARIANT_PARSE_ERROR_NO_COMMON_TYPE: could not find a common type for array entries
63 * @G_VARIANT_PARSE_ERROR_NUMBER_OUT_OF_RANGE: the numerical value is out of range of the given type
64 * @G_VARIANT_PARSE_ERROR_NUMBER_TOO_BIG: the numerical value is out of range for any type
65 * @G_VARIANT_PARSE_ERROR_TYPE_ERROR: cannot parse as variant of the specified type
66 * @G_VARIANT_PARSE_ERROR_UNEXPECTED_TOKEN: an unexpected token was encountered
67 * @G_VARIANT_PARSE_ERROR_UNKNOWN_KEYWORD: an unknown keyword was encountered
68 * @G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT: unterminated string constant
69 * @G_VARIANT_PARSE_ERROR_VALUE_EXPECTED: no value given
71 * Error codes returned by parsing text-format GVariants.
74 g_variant_parser_get_error_quark (void)
76 static GQuark the_quark;
79 the_quark = g_quark_from_static_string ("g-variant-parse-error-quark");
90 parser_set_error_va (GError **error,
97 GString *msg = g_string_new (NULL);
99 if (location->start == location->end)
100 g_string_append_printf (msg, "%d", location->start);
102 g_string_append_printf (msg, "%d-%d", location->start, location->end);
106 g_assert (other->start != other->end);
107 g_string_append_printf (msg, ",%d-%d", other->start, other->end);
109 g_string_append_c (msg, ':');
111 g_string_append_vprintf (msg, format, ap);
112 g_set_error_literal (error, G_VARIANT_PARSE_ERROR, code, msg->str);
113 g_string_free (msg, TRUE);
117 parser_set_error (GError **error,
126 va_start (ap, format);
127 parser_set_error_va (error, location, other, code, format, ap);
142 token_stream_set_error (TokenStream *stream,
152 ref.start = stream->this - stream->start;
155 ref.end = stream->stream - stream->start;
159 va_start (ap, format);
160 parser_set_error_va (error, &ref, NULL, code, format, ap);
165 token_stream_prepare (TokenStream *stream)
170 if (stream->this != NULL)
173 while (stream->stream != stream->end && g_ascii_isspace (*stream->stream))
176 if (stream->stream == stream->end || *stream->stream == '\0')
178 stream->this = stream->stream;
182 switch (stream->stream[0])
184 case '-': case '+': case '.': case '0': case '1': case '2':
185 case '3': case '4': case '5': case '6': case '7': case '8':
187 for (end = stream->stream; end != stream->end; end++)
188 if (!g_ascii_isalnum (*end) &&
189 *end != '-' && *end != '+' && *end != '.')
194 if (stream->stream[1] == '\'' || stream->stream[1] == '"')
196 for (end = stream->stream + 2; end != stream->end; end++)
197 if (*end == stream->stream[1] || *end == '\0' ||
198 (*end == '\\' && (++end == stream->end || *end == '\0')))
201 if (end != stream->end && *end)
208 case 'a': /* 'b' */ case 'c': case 'd': case 'e': case 'f':
209 case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
210 case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
211 case 's': case 't': case 'u': case 'v': case 'w': case 'x':
213 for (end = stream->stream; end != stream->end; end++)
214 if (!g_ascii_isalnum (*end))
219 for (end = stream->stream + 1; end != stream->end; end++)
220 if (*end == stream->stream[0] || *end == '\0' ||
221 (*end == '\\' && (++end == stream->end || *end == '\0')))
224 if (end != stream->end && *end)
229 /* stop at the first space, comma, colon or unmatched bracket.
230 * deals nicely with cases like (%i, %i) or {%i: %i}.
232 for (end = stream->stream + 1;
233 end != stream->end && *end != ',' &&
234 *end != ':' && *end != '>' && !g_ascii_isspace (*end);
237 if (*end == '(' || *end == '{')
240 else if ((*end == ')' || *end == '}') && !brackets--)
246 end = stream->stream + 1;
250 stream->this = stream->stream;
251 stream->stream = end;
257 token_stream_next (TokenStream *stream)
263 token_stream_peek (TokenStream *stream,
266 if (!token_stream_prepare (stream))
269 return stream->this[0] == first_char;
273 token_stream_peek2 (TokenStream *stream,
277 if (!token_stream_prepare (stream))
280 return stream->this[0] == first_char &&
281 stream->this[1] == second_char;
285 token_stream_is_keyword (TokenStream *stream)
287 if (!token_stream_prepare (stream))
290 return g_ascii_isalpha (stream->this[0]) &&
291 g_ascii_isalpha (stream->this[1]);
295 token_stream_is_numeric (TokenStream *stream)
297 if (!token_stream_prepare (stream))
300 return (g_ascii_isdigit (stream->this[0]) ||
301 stream->this[0] == '-' ||
302 stream->this[0] == '+' ||
303 stream->this[0] == '.');
307 token_stream_peek_string (TokenStream *stream,
310 gint length = strlen (token);
312 return token_stream_prepare (stream) &&
313 stream->stream - stream->this == length &&
314 memcmp (stream->this, token, length) == 0;
318 token_stream_consume (TokenStream *stream,
321 if (!token_stream_peek_string (stream, token))
324 token_stream_next (stream);
329 token_stream_require (TokenStream *stream,
331 const gchar *purpose,
335 if (!token_stream_consume (stream, token))
337 token_stream_set_error (stream, error, FALSE,
338 G_VARIANT_PARSE_ERROR_UNEXPECTED_TOKEN,
339 "expected `%s'%s", token, purpose);
347 token_stream_assert (TokenStream *stream,
350 gboolean correct_token;
352 correct_token = token_stream_consume (stream, token);
353 g_assert (correct_token);
357 token_stream_get (TokenStream *stream)
361 if (!token_stream_prepare (stream))
364 result = g_strndup (stream->this, stream->stream - stream->this);
370 token_stream_start_ref (TokenStream *stream,
373 token_stream_prepare (stream);
374 ref->start = stream->this - stream->start;
378 token_stream_end_ref (TokenStream *stream,
381 ref->end = stream->stream - stream->start;
385 pattern_copy (gchar **out,
390 while (**in == 'a' || **in == 'm' || **in == 'M')
391 *(*out)++ = *(*in)++;
395 if (**in == '(' || **in == '{')
398 else if (**in == ')' || **in == '}')
401 *(*out)++ = *(*in)++;
407 pattern_coalesce (const gchar *left,
413 /* the length of the output is loosely bound by the sum of the input
414 * lengths, not simply the greater of the two lengths.
416 * (*(iii)) + ((iii)*) ((iii)(iii))
420 out = result = g_malloc (strlen (left) + strlen (right));
422 while (*left && *right)
432 const gchar **one = &left, **the_other = &right;
435 if (**one == '*' && **the_other != ')')
437 pattern_copy (&out, the_other);
441 else if (**one == 'M' && **the_other == 'm')
443 *out++ = *(*the_other)++;
446 else if (**one == 'M' && **the_other != 'm')
451 else if (**one == 'N' && strchr ("ynqiuxthd", **the_other))
453 *out++ = *(*the_other)++;
457 else if (**one == 'S' && strchr ("sog", **the_other))
459 *out++ = *(*the_other)++;
463 else if (one == &left)
465 one = &right, the_other = &left;
485 typedef struct _AST AST;
486 typedef gchar * (*get_pattern_func) (AST *ast,
488 typedef GVariant * (*get_value_func) (AST *ast,
489 const GVariantType *type,
491 typedef GVariant * (*get_base_value_func) (AST *ast,
492 const GVariantType *type,
494 typedef void (*free_func) (AST *ast);
498 gchar * (* get_pattern) (AST *ast,
500 GVariant * (* get_value) (AST *ast,
501 const GVariantType *type,
503 GVariant * (* get_base_value) (AST *ast,
504 const GVariantType *type,
506 void (* free) (AST *ast);
511 const ASTClass *class;
512 SourceRef source_ref;
516 ast_get_pattern (AST *ast,
519 return ast->class->get_pattern (ast, error);
523 ast_get_value (AST *ast,
524 const GVariantType *type,
527 return ast->class->get_value (ast, type, error);
533 ast->class->free (ast);
537 ast_set_error (AST *ast,
546 va_start (ap, format);
547 parser_set_error_va (error, &ast->source_ref,
548 other_ast ? & other_ast->source_ref : NULL,
555 ast_type_error (AST *ast,
556 const GVariantType *type,
561 typestr = g_variant_type_dup_string (type);
562 ast_set_error (ast, error, NULL,
563 G_VARIANT_PARSE_ERROR_TYPE_ERROR,
564 "can not parse as value of type `%s'",
572 ast_resolve (AST *ast,
579 pattern = ast_get_pattern (ast, error);
584 /* choose reasonable defaults
586 * 1) favour non-maybe values where possible
587 * 2) default type for strings is 's'
588 * 3) default type for integers is 'i'
590 for (i = 0; pattern[i]; i++)
594 ast_set_error (ast, error, NULL,
595 G_VARIANT_PARSE_ERROR_CANNOT_INFER_TYPE,
596 "unable to infer type");
612 pattern[j++] = pattern[i];
617 value = ast_get_value (ast, G_VARIANT_TYPE (pattern), error);
624 static AST *parse (TokenStream *stream,
629 ast_array_append (AST ***array,
633 if ((*n_items & (*n_items - 1)) == 0)
634 *array = g_renew (AST *, *array, *n_items ? 2 ** n_items : 1);
636 (*array)[(*n_items)++] = ast;
640 ast_array_free (AST **array,
645 for (i = 0; i < n_items; i++)
651 ast_array_get_pattern (AST **array,
658 pattern = ast_get_pattern (array[0], error);
663 for (i = 1; i < n_items; i++)
667 tmp = ast_get_pattern (array[i], error);
675 merged = pattern_coalesce (pattern, tmp);
680 /* set coalescence implies pairwise coalescence (i think).
681 * we should therefore be able to trace the failure to a single
692 /* if 'j' reaches 'i' then we failed to find the pair */
695 tmp2 = ast_get_pattern (array[j], NULL);
696 g_assert (tmp2 != NULL);
698 m = pattern_coalesce (tmp, tmp2);
704 /* we found a conflict between 'i' and 'j'.
706 * report the error. note: 'j' is first.
708 ast_set_error (array[j], error, array[i],
709 G_VARIANT_PARSE_ERROR_NO_COMMON_TYPE,
710 "unable to find a common type");
734 maybe_get_pattern (AST *ast,
737 Maybe *maybe = (Maybe *) ast;
739 if (maybe->child != NULL)
741 gchar *child_pattern;
744 child_pattern = ast_get_pattern (maybe->child, error);
746 if (child_pattern == NULL)
749 pattern = g_strdup_printf ("m%s", child_pattern);
750 g_free (child_pattern);
755 return g_strdup ("m*");
759 maybe_get_value (AST *ast,
760 const GVariantType *type,
763 Maybe *maybe = (Maybe *) ast;
766 if (!g_variant_type_is_maybe (type))
767 return ast_type_error (ast, type, error);
769 type = g_variant_type_element (type);
773 value = ast_get_value (maybe->child, type, error);
781 return g_variant_new_maybe (type, value);
785 maybe_free (AST *ast)
787 Maybe *maybe = (Maybe *) ast;
789 if (maybe->child != NULL)
790 ast_free (maybe->child);
792 g_slice_free (Maybe, maybe);
796 maybe_parse (TokenStream *stream,
800 static const ASTClass maybe_class = {
802 maybe_get_value, NULL,
808 if (token_stream_consume (stream, "just"))
810 child = parse (stream, app, error);
815 else if (!token_stream_consume (stream, "nothing"))
817 token_stream_set_error (stream, error, TRUE,
818 G_VARIANT_PARSE_ERROR_UNKNOWN_KEYWORD,
823 maybe = g_slice_new (Maybe);
824 maybe->ast.class = &maybe_class;
825 maybe->child = child;
827 return (AST *) maybe;
831 maybe_wrapper (AST *ast,
832 const GVariantType *type,
835 const GVariantType *t;
839 for (depth = 0, t = type;
840 g_variant_type_is_maybe (t);
841 depth++, t = g_variant_type_element (t));
843 value = ast->class->get_base_value (ast, t, error);
849 value = g_variant_new_maybe (NULL, value);
863 array_get_pattern (AST *ast,
866 Array *array = (Array *) ast;
870 if (array->n_children == 0)
871 return g_strdup ("Ma*");
873 pattern = ast_array_get_pattern (array->children, array->n_children, error);
878 result = g_strdup_printf ("Ma%s", pattern);
885 array_get_value (AST *ast,
886 const GVariantType *type,
889 Array *array = (Array *) ast;
890 const GVariantType *childtype;
891 GVariantBuilder builder;
894 if (!g_variant_type_is_array (type))
895 return ast_type_error (ast, type, error);
897 g_variant_builder_init (&builder, type);
898 childtype = g_variant_type_element (type);
900 for (i = 0; i < array->n_children; i++)
904 if (!(child = ast_get_value (array->children[i], childtype, error)))
906 g_variant_builder_clear (&builder);
910 g_variant_builder_add_value (&builder, child);
913 return g_variant_builder_end (&builder);
917 array_free (AST *ast)
919 Array *array = (Array *) ast;
921 ast_array_free (array->children, array->n_children);
922 g_slice_free (Array, array);
926 array_parse (TokenStream *stream,
930 static const ASTClass array_class = {
932 maybe_wrapper, array_get_value,
935 gboolean need_comma = FALSE;
938 array = g_slice_new (Array);
939 array->ast.class = &array_class;
940 array->children = NULL;
941 array->n_children = 0;
943 token_stream_assert (stream, "[");
944 while (!token_stream_consume (stream, "]"))
949 !token_stream_require (stream, ",",
950 " or `]' to follow array element",
954 child = parse (stream, app, error);
959 ast_array_append (&array->children, &array->n_children, child);
963 return (AST *) array;
966 ast_array_free (array->children, array->n_children);
967 g_slice_free (Array, array);
981 tuple_get_pattern (AST *ast,
984 Tuple *tuple = (Tuple *) ast;
985 gchar *result = NULL;
989 parts = g_new (gchar *, tuple->n_children + 4);
990 parts[tuple->n_children + 1] = (gchar *) ")";
991 parts[tuple->n_children + 2] = NULL;
992 parts[0] = (gchar *) "M(";
994 for (i = 0; i < tuple->n_children; i++)
995 if (!(parts[i + 1] = ast_get_pattern (tuple->children[i], error)))
998 if (i == tuple->n_children)
999 result = g_strjoinv ("", parts);
1001 /* parts[0] should not be freed */
1003 g_free (parts[i--]);
1010 tuple_get_value (AST *ast,
1011 const GVariantType *type,
1014 Tuple *tuple = (Tuple *) ast;
1015 const GVariantType *childtype;
1016 GVariantBuilder builder;
1019 if (!g_variant_type_is_tuple (type))
1020 return ast_type_error (ast, type, error);
1022 g_variant_builder_init (&builder, type);
1023 childtype = g_variant_type_first (type);
1025 for (i = 0; i < tuple->n_children; i++)
1029 if (childtype == NULL)
1031 g_variant_builder_clear (&builder);
1032 return ast_type_error (ast, type, error);
1035 if (!(child = ast_get_value (tuple->children[i], childtype, error)))
1037 g_variant_builder_clear (&builder);
1041 g_variant_builder_add_value (&builder, child);
1042 childtype = g_variant_type_next (childtype);
1045 if (childtype != NULL)
1047 g_variant_builder_clear (&builder);
1048 return ast_type_error (ast, type, error);
1051 return g_variant_builder_end (&builder);
1055 tuple_free (AST *ast)
1057 Tuple *tuple = (Tuple *) ast;
1059 ast_array_free (tuple->children, tuple->n_children);
1060 g_slice_free (Tuple, tuple);
1064 tuple_parse (TokenStream *stream,
1068 static const ASTClass tuple_class = {
1070 maybe_wrapper, tuple_get_value,
1073 gboolean need_comma = FALSE;
1074 gboolean first = TRUE;
1077 tuple = g_slice_new (Tuple);
1078 tuple->ast.class = &tuple_class;
1079 tuple->children = NULL;
1080 tuple->n_children = 0;
1082 token_stream_assert (stream, "(");
1083 while (!token_stream_consume (stream, ")"))
1088 !token_stream_require (stream, ",",
1089 " or `)' to follow tuple element",
1093 child = parse (stream, app, error);
1098 ast_array_append (&tuple->children, &tuple->n_children, child);
1100 /* the first time, we absolutely require a comma, so grab it here
1101 * and leave need_comma = FALSE so that the code above doesn't
1102 * require a second comma.
1104 * the second and remaining times, we set need_comma = TRUE.
1108 if (!token_stream_require (stream, ",",
1109 " after first tuple element", error))
1118 return (AST *) tuple;
1121 ast_array_free (tuple->children, tuple->n_children);
1122 g_slice_free (Tuple, tuple);
1135 variant_get_pattern (AST *ast,
1138 return g_strdup ("Mv");
1142 variant_get_value (AST *ast,
1143 const GVariantType *type,
1146 Variant *variant = (Variant *) ast;
1149 g_assert (g_variant_type_equal (type, G_VARIANT_TYPE_VARIANT));
1150 child = ast_resolve (variant->value, error);
1155 return g_variant_new_variant (child);
1159 variant_free (AST *ast)
1161 Variant *variant = (Variant *) ast;
1163 ast_free (variant->value);
1164 g_slice_free (Variant, variant);
1168 variant_parse (TokenStream *stream,
1172 static const ASTClass variant_class = {
1173 variant_get_pattern,
1174 maybe_wrapper, variant_get_value,
1180 token_stream_assert (stream, "<");
1181 value = parse (stream, app, error);
1186 if (!token_stream_require (stream, ">", " to follow variant value", error))
1192 variant = g_slice_new (Variant);
1193 variant->ast.class = &variant_class;
1194 variant->value = value;
1196 return (AST *) variant;
1209 dictionary_get_pattern (AST *ast,
1212 Dictionary *dict = (Dictionary *) ast;
1213 gchar *value_pattern;
1218 if (dict->n_children == 0)
1219 return g_strdup ("Ma{**}");
1221 key_pattern = ast_array_get_pattern (dict->keys,
1222 abs (dict->n_children),
1225 if (key_pattern == NULL)
1228 /* we can not have maybe keys */
1229 if (key_pattern[0] == 'M')
1230 key_char = key_pattern[1];
1232 key_char = key_pattern[0];
1234 g_free (key_pattern);
1237 * plus undetermined number type and undetermined string type.
1239 if (!strchr ("bynqiuxthdsogNS", key_char))
1241 ast_set_error (ast, error, NULL,
1242 G_VARIANT_PARSE_ERROR_BASIC_TYPE_EXPECTED,
1243 "dictionary keys must have basic types");
1247 value_pattern = ast_get_pattern (dict->values[0], error);
1249 if (value_pattern == NULL)
1252 result = g_strdup_printf ("M%s{%c%s}",
1253 dict->n_children > 0 ? "a" : "",
1254 key_char, value_pattern);
1255 g_free (value_pattern);
1261 dictionary_get_value (AST *ast,
1262 const GVariantType *type,
1265 Dictionary *dict = (Dictionary *) ast;
1267 if (dict->n_children == -1)
1269 const GVariantType *subtype;
1270 GVariantBuilder builder;
1273 if (!g_variant_type_is_dict_entry (type))
1274 return ast_type_error (ast, type, error);
1276 g_variant_builder_init (&builder, type);
1278 subtype = g_variant_type_key (type);
1279 if (!(subvalue = ast_get_value (dict->keys[0], subtype, error)))
1281 g_variant_builder_clear (&builder);
1284 g_variant_builder_add_value (&builder, subvalue);
1286 subtype = g_variant_type_value (type);
1287 if (!(subvalue = ast_get_value (dict->values[0], subtype, error)))
1289 g_variant_builder_clear (&builder);
1292 g_variant_builder_add_value (&builder, subvalue);
1294 return g_variant_builder_end (&builder);
1298 const GVariantType *entry, *key, *val;
1299 GVariantBuilder builder;
1302 if (!g_variant_type_is_subtype_of (type, G_VARIANT_TYPE_DICTIONARY))
1303 return ast_type_error (ast, type, error);
1305 entry = g_variant_type_element (type);
1306 key = g_variant_type_key (entry);
1307 val = g_variant_type_value (entry);
1309 g_variant_builder_init (&builder, type);
1311 for (i = 0; i < dict->n_children; i++)
1315 g_variant_builder_open (&builder, entry);
1317 if (!(subvalue = ast_get_value (dict->keys[i], key, error)))
1319 g_variant_builder_clear (&builder);
1322 g_variant_builder_add_value (&builder, subvalue);
1324 if (!(subvalue = ast_get_value (dict->values[i], val, error)))
1326 g_variant_builder_clear (&builder);
1329 g_variant_builder_add_value (&builder, subvalue);
1330 g_variant_builder_close (&builder);
1333 return g_variant_builder_end (&builder);
1338 dictionary_free (AST *ast)
1340 Dictionary *dict = (Dictionary *) ast;
1343 if (dict->n_children > -1)
1344 n_children = dict->n_children;
1348 ast_array_free (dict->keys, n_children);
1349 ast_array_free (dict->values, n_children);
1350 g_slice_free (Dictionary, dict);
1354 dictionary_parse (TokenStream *stream,
1358 static const ASTClass dictionary_class = {
1359 dictionary_get_pattern,
1360 maybe_wrapper, dictionary_get_value,
1363 gint n_keys, n_values;
1368 dict = g_slice_new (Dictionary);
1369 dict->ast.class = &dictionary_class;
1371 dict->values = NULL;
1372 n_keys = n_values = 0;
1374 token_stream_assert (stream, "{");
1376 if (token_stream_consume (stream, "}"))
1378 dict->n_children = 0;
1379 return (AST *) dict;
1382 if ((first = parse (stream, app, error)) == NULL)
1385 ast_array_append (&dict->keys, &n_keys, first);
1387 only_one = token_stream_consume (stream, ",");
1389 !token_stream_require (stream, ":",
1390 " or `,' to follow dictionary entry key",
1394 if ((first = parse (stream, app, error)) == NULL)
1397 ast_array_append (&dict->values, &n_values, first);
1401 if (!token_stream_require (stream, "}", " at end of dictionary entry",
1405 g_assert (n_keys == 1 && n_values == 1);
1406 dict->n_children = -1;
1408 return (AST *) dict;
1411 while (!token_stream_consume (stream, "}"))
1415 if (!token_stream_require (stream, ",",
1416 " or `}' to follow dictionary entry", error))
1419 child = parse (stream, app, error);
1424 ast_array_append (&dict->keys, &n_keys, child);
1426 if (!token_stream_require (stream, ":",
1427 " to follow dictionary entry key", error))
1430 child = parse (stream, app, error);
1435 ast_array_append (&dict->values, &n_values, child);
1438 g_assert (n_keys == n_values);
1439 dict->n_children = n_keys;
1441 return (AST *) dict;
1444 ast_array_free (dict->keys, n_keys);
1445 ast_array_free (dict->values, n_values);
1446 g_slice_free (Dictionary, dict);
1458 string_get_pattern (AST *ast,
1461 return g_strdup ("MS");
1465 string_get_value (AST *ast,
1466 const GVariantType *type,
1469 String *string = (String *) ast;
1471 if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
1472 return g_variant_new_string (string->string);
1474 else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
1476 if (!g_variant_is_object_path (string->string))
1478 ast_set_error (ast, error, NULL,
1479 G_VARIANT_PARSE_ERROR_INVALID_OBJECT_PATH,
1480 "not a valid object path");
1484 return g_variant_new_object_path (string->string);
1487 else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
1489 if (!g_variant_is_signature (string->string))
1491 ast_set_error (ast, error, NULL,
1492 G_VARIANT_PARSE_ERROR_INVALID_SIGNATURE,
1493 "not a valid signature");
1497 return g_variant_new_signature (string->string);
1501 return ast_type_error (ast, type, error);
1505 string_free (AST *ast)
1507 String *string = (String *) ast;
1509 g_free (string->string);
1510 g_slice_free (String, string);
1514 unicode_unescape (const gchar *src,
1528 g_assert (length < sizeof (buffer));
1529 strncpy (buffer, src + *src_ofs, length);
1530 buffer[length] = '\0';
1532 value = g_ascii_strtoull (buffer, &end, 0x10);
1534 if (value == 0 || end != buffer + length)
1536 parser_set_error (error, ref, NULL,
1537 G_VARIANT_PARSE_ERROR_INVALID_CHARACTER,
1538 "invalid %d-character unicode escape", length);
1542 g_assert (value <= G_MAXUINT32);
1544 *dest_ofs += g_unichar_to_utf8 (value, dest + *dest_ofs);
1551 string_parse (TokenStream *stream,
1555 static const ASTClass string_class = {
1557 maybe_wrapper, string_get_value,
1568 token_stream_start_ref (stream, &ref);
1569 token = token_stream_get (stream);
1570 token_stream_end_ref (stream, &ref);
1571 length = strlen (token);
1574 str = g_malloc (length);
1575 g_assert (quote == '"' || quote == '\'');
1578 while (token[i] != quote)
1582 parser_set_error (error, &ref, NULL,
1583 G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
1584 "unterminated string constant");
1593 parser_set_error (error, &ref, NULL,
1594 G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
1595 "unterminated string constant");
1601 if (!unicode_unescape (token, &i, str, &j, 4, &ref, error))
1610 if (!unicode_unescape (token, &i, str, &j, 8, &ref, error))
1618 case 'a': str[j++] = '\a'; i++; continue;
1619 case 'b': str[j++] = '\b'; i++; continue;
1620 case 'f': str[j++] = '\f'; i++; continue;
1621 case 'n': str[j++] = '\n'; i++; continue;
1622 case 'r': str[j++] = '\r'; i++; continue;
1623 case 't': str[j++] = '\t'; i++; continue;
1624 case 'v': str[j++] = '\v'; i++; continue;
1625 case '\n': i++; continue;
1629 str[j++] = token[i++];
1634 string = g_slice_new (String);
1635 string->ast.class = &string_class;
1636 string->string = str;
1638 token_stream_next (stream);
1640 return (AST *) string;
1650 bytestring_get_pattern (AST *ast,
1653 return g_strdup ("May");
1657 bytestring_get_value (AST *ast,
1658 const GVariantType *type,
1661 ByteString *string = (ByteString *) ast;
1663 g_assert (g_variant_type_equal (type, G_VARIANT_TYPE_BYTESTRING));
1665 return g_variant_new_bytestring (string->string);
1669 bytestring_free (AST *ast)
1671 ByteString *string = (ByteString *) ast;
1673 g_free (string->string);
1674 g_slice_free (ByteString, string);
1678 bytestring_parse (TokenStream *stream,
1682 static const ASTClass bytestring_class = {
1683 bytestring_get_pattern,
1684 maybe_wrapper, bytestring_get_value,
1695 token_stream_start_ref (stream, &ref);
1696 token = token_stream_get (stream);
1697 token_stream_end_ref (stream, &ref);
1698 g_assert (token[0] == 'b');
1699 length = strlen (token);
1702 str = g_malloc (length);
1703 g_assert (quote == '"' || quote == '\'');
1706 while (token[i] != quote)
1710 parser_set_error (error, &ref, NULL,
1711 G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
1712 "unterminated string constant");
1720 parser_set_error (error, &ref, NULL,
1721 G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
1722 "unterminated string constant");
1726 case '0': case '1': case '2': case '3':
1727 case '4': case '5': case '6': case '7':
1729 /* up to 3 characters */
1730 guchar val = token[i++] - '0';
1732 if ('0' <= token[i] && token[i] < '8')
1733 val = (val << 3) | (token[i++] - '0');
1735 if ('0' <= token[i] && token[i] < '8')
1736 val = (val << 3) | (token[i++] - '0');
1742 case 'a': str[j++] = '\a'; i++; continue;
1743 case 'b': str[j++] = '\b'; i++; continue;
1744 case 'f': str[j++] = '\f'; i++; continue;
1745 case 'n': str[j++] = '\n'; i++; continue;
1746 case 'r': str[j++] = '\r'; i++; continue;
1747 case 't': str[j++] = '\t'; i++; continue;
1748 case 'v': str[j++] = '\v'; i++; continue;
1749 case '\n': i++; continue;
1753 str[j++] = token[i++];
1758 string = g_slice_new (ByteString);
1759 string->ast.class = &bytestring_class;
1760 string->string = str;
1762 token_stream_next (stream);
1764 return (AST *) string;
1775 number_get_pattern (AST *ast,
1778 Number *number = (Number *) ast;
1780 if (strchr (number->token, '.') ||
1781 (!g_str_has_prefix (number->token, "0x") && strchr (number->token, 'e')) ||
1782 strstr (number->token, "inf") ||
1783 strstr (number->token, "nan"))
1784 return g_strdup ("Md");
1786 return g_strdup ("MN");
1790 number_overflow (AST *ast,
1791 const GVariantType *type,
1794 ast_set_error (ast, error, NULL,
1795 G_VARIANT_PARSE_ERROR_NUMBER_OUT_OF_RANGE,
1796 "number out of range for type `%c'",
1797 g_variant_type_peek_string (type)[0]);
1802 number_get_value (AST *ast,
1803 const GVariantType *type,
1806 Number *number = (Number *) ast;
1814 token = number->token;
1816 if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
1821 dbl_val = g_ascii_strtod (token, &end);
1822 if (dbl_val != 0.0 && errno == ERANGE)
1824 ast_set_error (ast, error, NULL,
1825 G_VARIANT_PARSE_ERROR_NUMBER_TOO_BIG,
1826 "number too big for any type");
1830 /* silence uninitialised warnings... */
1837 negative = token[0] == '-';
1838 if (token[0] == '-')
1842 abs_val = g_ascii_strtoull (token, &end, 0);
1843 if (abs_val == G_MAXUINT64 && errno == ERANGE)
1845 ast_set_error (ast, error, NULL,
1846 G_VARIANT_PARSE_ERROR_NUMBER_TOO_BIG,
1847 "integer too big for any type");
1854 /* silence uninitialised warning... */
1862 ref = ast->source_ref;
1863 ref.start += end - number->token;
1864 ref.end = ref.start + 1;
1866 parser_set_error (error, &ref, NULL,
1867 G_VARIANT_PARSE_ERROR_INVALID_CHARACTER,
1868 "invalid character in number");
1873 return g_variant_new_double (dbl_val);
1875 switch (*g_variant_type_peek_string (type))
1878 if (negative || abs_val > G_MAXUINT8)
1879 return number_overflow (ast, type, error);
1880 return g_variant_new_byte (abs_val);
1883 if (abs_val - negative > G_MAXINT16)
1884 return number_overflow (ast, type, error);
1885 return g_variant_new_int16 (negative ? -abs_val : abs_val);
1888 if (negative || abs_val > G_MAXUINT16)
1889 return number_overflow (ast, type, error);
1890 return g_variant_new_uint16 (abs_val);
1893 if (abs_val - negative > G_MAXINT32)
1894 return number_overflow (ast, type, error);
1895 return g_variant_new_int32 (negative ? -abs_val : abs_val);
1898 if (negative || abs_val > G_MAXUINT32)
1899 return number_overflow (ast, type, error);
1900 return g_variant_new_uint32 (abs_val);
1903 if (abs_val - negative > G_MAXINT64)
1904 return number_overflow (ast, type, error);
1905 return g_variant_new_int64 (negative ? -abs_val : abs_val);
1909 return number_overflow (ast, type, error);
1910 return g_variant_new_uint64 (abs_val);
1913 if (abs_val - negative > G_MAXINT32)
1914 return number_overflow (ast, type, error);
1915 return g_variant_new_handle (negative ? -abs_val : abs_val);
1918 return ast_type_error (ast, type, error);
1923 number_free (AST *ast)
1925 Number *number = (Number *) ast;
1927 g_free (number->token);
1928 g_slice_free (Number, number);
1932 number_parse (TokenStream *stream,
1936 static const ASTClass number_class = {
1938 maybe_wrapper, number_get_value,
1943 number = g_slice_new (Number);
1944 number->ast.class = &number_class;
1945 number->token = token_stream_get (stream);
1946 token_stream_next (stream);
1948 return (AST *) number;
1958 boolean_get_pattern (AST *ast,
1961 return g_strdup ("Mb");
1965 boolean_get_value (AST *ast,
1966 const GVariantType *type,
1969 Boolean *boolean = (Boolean *) ast;
1971 if (!g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
1972 return ast_type_error (ast, type, error);
1974 return g_variant_new_boolean (boolean->value);
1978 boolean_free (AST *ast)
1980 Boolean *boolean = (Boolean *) ast;
1982 g_slice_free (Boolean, boolean);
1986 boolean_new (gboolean value)
1988 static const ASTClass boolean_class = {
1989 boolean_get_pattern,
1990 maybe_wrapper, boolean_get_value,
1995 boolean = g_slice_new (Boolean);
1996 boolean->ast.class = &boolean_class;
1997 boolean->value = value;
1999 return (AST *) boolean;
2010 positional_get_pattern (AST *ast,
2013 Positional *positional = (Positional *) ast;
2015 return g_strdup (g_variant_get_type_string (positional->value));
2019 positional_get_value (AST *ast,
2020 const GVariantType *type,
2023 Positional *positional = (Positional *) ast;
2026 g_assert (positional->value != NULL);
2028 if G_UNLIKELY (!g_variant_is_of_type (positional->value, type))
2029 return ast_type_error (ast, type, error);
2031 /* NOTE: if _get is called more than once then
2032 * things get messed up with respect to floating refs.
2034 * fortunately, this function should only ever get called once.
2036 g_assert (positional->value != NULL);
2037 value = positional->value;
2038 positional->value = NULL;
2044 positional_free (AST *ast)
2046 Positional *positional = (Positional *) ast;
2048 /* if positional->value is set, just leave it.
2049 * memory management doesn't matter in case of programmer error.
2051 g_slice_free (Positional, positional);
2055 positional_parse (TokenStream *stream,
2059 static const ASTClass positional_class = {
2060 positional_get_pattern,
2061 positional_get_value, NULL,
2064 Positional *positional;
2065 const gchar *endptr;
2068 token = token_stream_get (stream);
2069 g_assert (token[0] == '%');
2071 positional = g_slice_new (Positional);
2072 positional->ast.class = &positional_class;
2073 positional->value = g_variant_new_va (token + 1, &endptr, app);
2075 if (*endptr || positional->value == NULL)
2077 token_stream_set_error (stream, error, TRUE,
2078 G_VARIANT_PARSE_ERROR_INVALID_FORMAT_STRING,
2079 "invalid GVariant format string");
2080 /* memory management doesn't matter in case of programmer error. */
2084 token_stream_next (stream);
2087 return (AST *) positional;
2099 typedecl_get_pattern (AST *ast,
2102 TypeDecl *decl = (TypeDecl *) ast;
2104 return g_variant_type_dup_string (decl->type);
2108 typedecl_get_value (AST *ast,
2109 const GVariantType *type,
2112 TypeDecl *decl = (TypeDecl *) ast;
2114 return ast_get_value (decl->child, type, error);
2118 typedecl_free (AST *ast)
2120 TypeDecl *decl = (TypeDecl *) ast;
2122 ast_free (decl->child);
2123 g_variant_type_free (decl->type);
2124 g_slice_free (TypeDecl, decl);
2128 typedecl_parse (TokenStream *stream,
2132 static const ASTClass typedecl_class = {
2133 typedecl_get_pattern,
2134 typedecl_get_value, NULL,
2141 if (token_stream_peek (stream, '@'))
2145 token = token_stream_get (stream);
2147 if (!g_variant_type_string_is_valid (token + 1))
2149 token_stream_set_error (stream, error, TRUE,
2150 G_VARIANT_PARSE_ERROR_INVALID_TYPE_STRING,
2151 "invalid type declaration");
2157 type = g_variant_type_new (token + 1);
2159 if (!g_variant_type_is_definite (type))
2161 token_stream_set_error (stream, error, TRUE,
2162 G_VARIANT_PARSE_ERROR_DEFINITE_TYPE_EXPECTED,
2163 "type declarations must be definite");
2164 g_variant_type_free (type);
2170 token_stream_next (stream);
2175 if (token_stream_consume (stream, "boolean"))
2176 type = g_variant_type_copy (G_VARIANT_TYPE_BOOLEAN);
2178 else if (token_stream_consume (stream, "byte"))
2179 type = g_variant_type_copy (G_VARIANT_TYPE_BYTE);
2181 else if (token_stream_consume (stream, "int16"))
2182 type = g_variant_type_copy (G_VARIANT_TYPE_INT16);
2184 else if (token_stream_consume (stream, "uint16"))
2185 type = g_variant_type_copy (G_VARIANT_TYPE_UINT16);
2187 else if (token_stream_consume (stream, "int32"))
2188 type = g_variant_type_copy (G_VARIANT_TYPE_INT32);
2190 else if (token_stream_consume (stream, "handle"))
2191 type = g_variant_type_copy (G_VARIANT_TYPE_HANDLE);
2193 else if (token_stream_consume (stream, "uint32"))
2194 type = g_variant_type_copy (G_VARIANT_TYPE_UINT32);
2196 else if (token_stream_consume (stream, "int64"))
2197 type = g_variant_type_copy (G_VARIANT_TYPE_INT64);
2199 else if (token_stream_consume (stream, "uint64"))
2200 type = g_variant_type_copy (G_VARIANT_TYPE_UINT64);
2202 else if (token_stream_consume (stream, "double"))
2203 type = g_variant_type_copy (G_VARIANT_TYPE_DOUBLE);
2205 else if (token_stream_consume (stream, "string"))
2206 type = g_variant_type_copy (G_VARIANT_TYPE_STRING);
2208 else if (token_stream_consume (stream, "objectpath"))
2209 type = g_variant_type_copy (G_VARIANT_TYPE_OBJECT_PATH);
2211 else if (token_stream_consume (stream, "signature"))
2212 type = g_variant_type_copy (G_VARIANT_TYPE_SIGNATURE);
2216 token_stream_set_error (stream, error, TRUE,
2217 G_VARIANT_PARSE_ERROR_UNKNOWN_KEYWORD,
2223 if ((child = parse (stream, app, error)) == NULL)
2225 g_variant_type_free (type);
2229 decl = g_slice_new (TypeDecl);
2230 decl->ast.class = &typedecl_class;
2232 decl->child = child;
2234 return (AST *) decl;
2238 parse (TokenStream *stream,
2242 SourceRef source_ref;
2245 token_stream_prepare (stream);
2246 token_stream_start_ref (stream, &source_ref);
2248 if (token_stream_peek (stream, '['))
2249 result = array_parse (stream, app, error);
2251 else if (token_stream_peek (stream, '('))
2252 result = tuple_parse (stream, app, error);
2254 else if (token_stream_peek (stream, '<'))
2255 result = variant_parse (stream, app, error);
2257 else if (token_stream_peek (stream, '{'))
2258 result = dictionary_parse (stream, app, error);
2260 else if (app && token_stream_peek (stream, '%'))
2261 result = positional_parse (stream, app, error);
2263 else if (token_stream_consume (stream, "true"))
2264 result = boolean_new (TRUE);
2266 else if (token_stream_consume (stream, "false"))
2267 result = boolean_new (FALSE);
2269 else if (token_stream_is_numeric (stream) ||
2270 token_stream_peek_string (stream, "inf") ||
2271 token_stream_peek_string (stream, "nan"))
2272 result = number_parse (stream, app, error);
2274 else if (token_stream_peek (stream, 'n') ||
2275 token_stream_peek (stream, 'j'))
2276 result = maybe_parse (stream, app, error);
2278 else if (token_stream_peek (stream, '@') ||
2279 token_stream_is_keyword (stream))
2280 result = typedecl_parse (stream, app, error);
2282 else if (token_stream_peek (stream, '\'') ||
2283 token_stream_peek (stream, '"'))
2284 result = string_parse (stream, app, error);
2286 else if (token_stream_peek2 (stream, 'b', '\'') ||
2287 token_stream_peek2 (stream, 'b', '"'))
2288 result = bytestring_parse (stream, app, error);
2292 token_stream_set_error (stream, error, FALSE,
2293 G_VARIANT_PARSE_ERROR_VALUE_EXPECTED,
2300 token_stream_end_ref (stream, &source_ref);
2301 result->source_ref = source_ref;
2309 * @type: a #GVariantType, or %NULL
2310 * @text: a string containing a GVariant in text form
2311 * @limit: a pointer to the end of @text, or %NULL
2312 * @endptr: a location to store the end pointer, or %NULL
2313 * @error: a pointer to a %NULL #GError pointer, or %NULL
2314 * @Returns: a reference to a #GVariant, or %NULL
2316 * Parses a #GVariant from a text representation.
2318 * A single #GVariant is parsed from the content of @text.
2320 * The format is described <link linkend='gvariant-text'>here</link>.
2322 * The memory at @limit will never be accessed and the parser behaves as
2323 * if the character at @limit is the nul terminator. This has the
2324 * effect of bounding @text.
2326 * If @endptr is non-%NULL then @text is permitted to contain data
2327 * following the value that this function parses and @endptr will be
2328 * updated to point to the first character past the end of the text
2329 * parsed by this function. If @endptr is %NULL and there is extra data
2330 * then an error is returned.
2332 * If @type is non-%NULL then the value will be parsed to have that
2333 * type. This may result in additional parse errors (in the case that
2334 * the parsed value doesn't fit the type) but may also result in fewer
2335 * errors (in the case that the type would have been ambiguous, such as
2336 * with empty arrays).
2338 * In the event that the parsing is successful, the resulting #GVariant
2341 * In case of any error, %NULL will be returned. If @error is non-%NULL
2342 * then it will be set to reflect the error that occurred.
2344 * Officially, the language understood by the parser is "any string
2345 * produced by g_variant_print()".
2348 g_variant_parse (const GVariantType *type,
2351 const gchar **endptr,
2354 TokenStream stream = { 0, };
2355 GVariant *result = NULL;
2358 g_return_val_if_fail (text != NULL, NULL);
2359 g_return_val_if_fail (text == limit || text != NULL, NULL);
2361 stream.start = text;
2362 stream.stream = text;
2365 if ((ast = parse (&stream, NULL, error)))
2368 result = ast_resolve (ast, error);
2370 result = ast_get_value (ast, type, error);
2374 g_variant_ref_sink (result);
2378 while (stream.stream != limit &&
2379 g_ascii_isspace (*stream.stream))
2382 if (stream.stream != limit && *stream.stream != '\0')
2384 SourceRef ref = { stream.stream - text,
2385 stream.stream - text };
2387 parser_set_error (error, &ref, NULL,
2388 G_VARIANT_PARSE_ERROR_INPUT_NOT_AT_END,
2389 "expected end of input");
2390 g_variant_unref (result);
2396 *endptr = stream.stream;
2406 * g_variant_new_parsed_va:
2407 * @format: a text format #GVariant
2408 * @app: a pointer to a #va_list
2410 * Parses @format and returns the result.
2412 * This is the version of g_variant_new_parsed() intended to be used
2415 * The return value will be floating if it was a newly created GVariant
2416 * instance. In the case that @format simply specified the collection
2417 * of a #GVariant pointer (eg: @format was "%*") then the collected
2418 * #GVariant pointer will be returned unmodified, without adding any
2419 * additional references.
2421 * In order to behave correctly in all cases it is necessary for the
2422 * calling function to g_variant_ref_sink() the return result before
2423 * returning control to the user that originally provided the pointer.
2424 * At this point, the caller will have their own full reference to the
2425 * result. This can also be done by adding the result to a container,
2426 * or by passing it to another g_variant_new() call.
2428 * Returns: a new, usually floating, #GVariant
2431 g_variant_new_parsed_va (const gchar *format,
2434 TokenStream stream = { 0, };
2435 GVariant *result = NULL;
2436 GError *error = NULL;
2439 g_return_val_if_fail (format != NULL, NULL);
2440 g_return_val_if_fail (app != NULL, NULL);
2442 stream.start = format;
2443 stream.stream = format;
2446 if ((ast = parse (&stream, app, &error)))
2448 result = ast_resolve (ast, &error);
2453 g_error ("g_variant_new_parsed: %s", error->message);
2456 g_error ("g_variant_new_parsed: trailing text after value");
2462 * g_variant_new_parsed:
2463 * @format: a text format #GVariant
2464 * @...: arguments as per @format
2466 * Parses @format and returns the result.
2468 * @format must be a text format #GVariant with one extension: at any
2469 * point that a value may appear in the text, a '%' character followed
2470 * by a GVariant format string (as per g_variant_new()) may appear. In
2471 * that case, the same arguments are collected from the argument list as
2472 * g_variant_new() would have collected.
2474 * Consider this simple example:
2476 * <informalexample><programlisting>
2477 * g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
2478 * </programlisting></informalexample>
2480 * In the example, the variable argument parameters are collected and
2481 * filled in as if they were part of the original string to produce the
2482 * result of <code>[('one', 1), ('two', 2), ('three', 3)]</code>.
2484 * This function is intended only to be used with @format as a string
2485 * literal. Any parse error is fatal to the calling process. If you
2486 * want to parse data from untrusted sources, use g_variant_parse().
2488 * You may not use this function to return, unmodified, a single
2489 * #GVariant pointer from the argument list. ie: @format may not solely
2490 * be anything along the lines of "%*", "%?", "\%r", or anything starting
2493 * Returns: a new floating #GVariant instance
2496 g_variant_new_parsed (const gchar *format,
2502 va_start (ap, format);
2503 result = g_variant_new_parsed_va (format, &ap);
2510 * g_variant_builder_add_parsed:
2511 * @builder: a #GVariantBuilder
2512 * @format: a text format #GVariant
2513 * @...: arguments as per @format
2515 * Adds to a #GVariantBuilder.
2517 * This call is a convenience wrapper that is exactly equivalent to
2518 * calling g_variant_new_parsed() followed by
2519 * g_variant_builder_add_value().
2521 * This function might be used as follows:
2525 * make_pointless_dictionary (void)
2527 * GVariantBuilder *builder;
2530 * builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
2531 * g_variant_builder_add_parsed (builder, "{'width', <%i>}", 600);
2532 * g_variant_builder_add_parsed (builder, "{'title', <%s>}", "foo");
2533 * g_variant_builder_add_parsed (builder, "{'transparency', <0.5>}");
2534 * return g_variant_builder_end (builder);
2541 g_variant_builder_add_parsed (GVariantBuilder *builder,
2542 const gchar *format,
2547 va_start (ap, format);
2548 g_variant_builder_add_value (builder, g_variant_new_parsed_va (format, &ap));