cli: allow map values in --params arguments
authorSimon Marchi <simon.marchi@efficios.com>
Thu, 15 Aug 2019 04:00:15 +0000 (00:00 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Sat, 17 Aug 2019 00:12:13 +0000 (20:12 -0400)
This patch makes it possible to use map values in --params arguments.
For example:

    --params 'a={hi="mlady",mapping={tete="epaule",genou="orteil"}}'

The chosen syntax uses curly braces to delimit the map.  It uses an
equal sign between the key an value, to be consistent with the syntax of
the implicit map at the top-level of `--params` values.

Speaking of which: it's not done in this patch, but it should be
possible to make the new function a bit more generic and re-use it for
parsing the top-level dict of `--params` values.

Change-Id: I1400ff6a2cf6c9132a9c5dd731a462f43225146b
Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/1936
Tested-by: jenkins <jenkins@lttng.org>
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
src/cli/babeltrace2-cfg-cli-args.c
src/cli/babeltrace2-cfg-cli-params-arg.c
tests/cli/params/test_params

index 1e2da506ad49722fe2a9535bc9ba041bed920e46..1bc0aade21ea532c25e8c511cbde92d3b067e858 100644 (file)
@@ -1300,6 +1300,8 @@ void print_expected_params_format(FILE *fp)
        fprintf(fp, "* Double-quoted string (accepts escape characters).\n");
        fprintf(fp, "* Array, formatted as an opening `[`, a list of comma-separated values\n");
        fprintf(fp, "  (as described by the current list) and a closing `]`.\n");
+       fprintf(fp, "* Map, formatted as an opening `{`, a comma-separated list of PARAM=VALUE\n");
+       fprintf(fp, "  assignments and a closing `}`.\n");
        fprintf(fp, "\n");
        fprintf(fp, "You can put whitespaces allowed around individual `=` and `,` symbols.\n");
        fprintf(fp, "\n");
index 79cde92970a53d49d221fc9e100daf61c3abc941..bd953c3a932ada0be95adb6d7494634c240ee840 100644 (file)
@@ -253,6 +253,110 @@ end:
        return array_value;
 }
 
+/*
+ * Parses the current and following tokens as a map. Maps are
+ * formatted as an opening `{`, a list of comma-separated entries, and a
+ * closing `}`. And entry is a key (an unquoted string), an equal sign and
+ * a value. For convenience, this function supports an optional trailing comma
+ * after the last value.
+ *
+ * The current token of the parser must be the opening curly bracket
+ * (`{`) of the array.
+ */
+static
+bt_value *ini_parse_map(struct ini_parsing_state *state)
+{
+       bt_value *map_value;
+       GTokenType token_type;
+       gchar *key = NULL;
+
+       /* The `{` character must have already been ingested */
+       BT_ASSERT(g_scanner_cur_token(state->scanner) == G_TOKEN_CHAR);
+       BT_ASSERT(g_scanner_cur_value(state->scanner).v_char == '{');
+
+       map_value = bt_value_map_create ();
+       if (!map_value) {
+               ini_append_oom_error(state->ini_error);
+               goto error;
+       }
+
+       token_type = g_scanner_get_next_token(state->scanner);
+
+       /* While the current token is not a `}` */
+       while (!(token_type == G_TOKEN_CHAR &&
+                       g_scanner_cur_value(state->scanner).v_char == '}')) {
+               bt_value *entry_value;
+               bt_value_map_insert_entry_status insert_entry_status;
+
+               /* Expect map key. */
+               if (token_type != G_TOKEN_IDENTIFIER) {
+                       ini_append_error_expecting(state, state->scanner,
+                               "unquoted map key");
+                       goto error;
+               }
+
+               g_free(key);
+               key = g_strdup(g_scanner_cur_value(state->scanner).v_identifier);
+
+               token_type = g_scanner_get_next_token(state->scanner);
+
+               /* Expect equal sign. */
+               if (token_type != G_TOKEN_CHAR ||
+                               g_scanner_cur_value(state->scanner).v_char != '=') {
+                       ini_append_error_expecting(state,
+                               state->scanner, "'='");
+                       goto error;
+               }
+
+               token_type = g_scanner_get_next_token(state->scanner);
+
+               /* Parse the entry value... */
+               entry_value = ini_parse_value(state);
+               if (!entry_value) {
+                       goto error;
+               }
+
+               /* ... and add it to the result map */
+               insert_entry_status =
+                       bt_value_map_insert_entry(map_value, key, entry_value);
+               BT_VALUE_PUT_REF_AND_RESET(entry_value);
+               if (insert_entry_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+                       goto error;
+               }
+
+               /*
+                * Ingest the token following the value. It should be
+                * either a comma or closing curly bracket.
+                */
+               token_type = g_scanner_get_next_token(state->scanner);
+               if (token_type == G_TOKEN_CHAR &&
+                               g_scanner_cur_value(state->scanner).v_char == ',') {
+                       /*
+                        * Ingest the token following the comma. If it
+                        * happens to be a closing curly bracket, exit
+                        * the loop and we are done (we allow trailing
+                        * commas). Otherwise, we are ready for the next
+                        * ini_parse_value() call.
+                        */
+                       token_type = g_scanner_get_next_token(state->scanner);
+               } else if (token_type != G_TOKEN_CHAR ||
+                               g_scanner_cur_value(state->scanner).v_char != '}') {
+                       ini_append_error_expecting(state, state->scanner,
+                               "`,` or `}`");
+                       goto error;
+               }
+       }
+
+       goto end;
+error:
+       BT_VALUE_PUT_REF_AND_RESET(map_value);
+
+end:
+       g_free(key);
+
+       return map_value;
+}
+
 /*
  * Parses the current token (and the following ones if needed) as a
  * value, returning it as a `bt_value *`.
@@ -274,6 +378,9 @@ bt_value *ini_parse_value(struct ini_parsing_state *state)
                } else if (state->scanner->value.v_char == '[') {
                        /* Array */
                        value = ini_parse_array(state);
+               } else if (state->scanner->value.v_char == '{') {
+                       /* Map */
+                       value = ini_parse_map(state);
                } else {
                        ini_append_error_expecting(state, state->scanner, "value");
                        goto end;
index 273b6244e9cf78a90c107ec6f2ed2ef501168f0f..7403fea719ea62d9d5c52af91f160f4b24831c4f 100755 (executable)
@@ -27,7 +27,7 @@ fi
 # shellcheck source=../../utils/utils.sh
 SH_TAP=1 source "$UTILSSH"
 
-NUM_TESTS=8
+NUM_TESTS=9
 
 plan_tests $NUM_TESTS
 
@@ -65,5 +65,7 @@ expect_success 'float scientific notation' 'a=10.5e6, b=10.5E6, c=10.5e-6, d=10.
        '{a=10500000.0, b=10500000.0, c=1.05e-05, d=1.05e-05}'
 expect_success 'array' 'a=[1, [["hi",]]]' \
        '{a=[1, [[hi]]]}'
+expect_success 'map' 'a={},b={salut="la gang",comment="ca va",oh={x=2}}' \
+       '{a={}, b={comment=ca va, oh={x=2}, salut=la gang}}'
 
 rm -f "$expected_file"
This page took 0.028852 seconds and 4 git commands to generate.