diff options
author | Matthias Vogelgesang <matthias.vogelgesang@kit.edu> | 2016-10-27 12:45:58 +0200 |
---|---|---|
committer | Matthias Vogelgesang <matthias.vogelgesang@kit.edu> | 2016-10-27 12:45:58 +0200 |
commit | 525815d1b7fe194e25361f9984c9ad2b97caac2e (patch) | |
tree | 12e622d1051f1d8cc1f9b2ab6cae4871102218b9 /bin | |
parent | 35a6c84ed07055ca484b3d3798d20f6bdad6a5ae (diff) |
ufo-launch: rewrite specification parser
This allows more flexible workflow descriptions like
ufo-launch \
[ [ dummy-data, dummy-data, dummy-data ] ! ffc, \
dummy-data, \
dummy-data ] ! ffc ! null
Diffstat (limited to 'bin')
-rw-r--r-- | bin/ufo-launch.c | 335 |
1 files changed, 227 insertions, 108 deletions
diff --git a/bin/ufo-launch.c b/bin/ufo-launch.c index d80d899..d3e172c 100644 --- a/bin/ufo-launch.c +++ b/bin/ufo-launch.c @@ -28,11 +28,6 @@ #include "config.h" -typedef struct { - char *name; - GList *props; -} TaskDescription; - static gboolean str_to_boolean (const gchar *s) @@ -69,13 +64,19 @@ typedef enum { COMMA = 1 << 6 } TokenType; - typedef struct { TokenType type; GString *str; guint pos; } Token; +typedef struct { + UfoPluginManager *pm; + UfoTaskGraph *graph; + GList *current; + GError **error; +} Environment; + static GList * tokenize_args (const gchar *pipeline) @@ -160,126 +161,244 @@ set_property (UfoTaskNode *task, const gchar *key, const gchar *pvalue) g_value_unset (&value); } -static UfoTaskGraph * -parse (const gchar *pipeline, GList *tokens, UfoPluginManager *pm, GError **error) +static Token * +token (GList *it) { - GList *it; - GQueue *stack; - UfoTaskGraph *graph; - UfoTaskNode *task = NULL; - gchar *key = NULL; - GList *group = NULL; + return (it == NULL) ? NULL : (Token *) it->data; +} - stack = g_queue_new (); +static Token * +consume (Environment *env) +{ + Token *t = token (env->current); + env->current = env->current->next; + return t; +} - typedef enum { - NEW_TASK = 1, - PROP_KEY = 1 << 1, - PROP_VALUE = 1 << 2, - CONNECT = 1 << 3 - } Action; +static Token * +peek (Environment *env) +{ + return token (env->current); +} - Action action = NEW_TASK; +static void +skip (Environment *env) +{ + env->current = env->current->next; +} - gint expected = PAREN_OPEN | STRING; +static void +consume_spaces (Environment *env) +{ + while (env->current != NULL && (token (env->current))->type == SPACE) + env->current = env->current->next; +} - g_value_register_transform_func (G_TYPE_STRING, G_TYPE_UCHAR, value_transform_uchar); - g_value_register_transform_func (G_TYPE_STRING, G_TYPE_INT, value_transform_int); - g_value_register_transform_func (G_TYPE_STRING, G_TYPE_UINT, value_transform_uint); - g_value_register_transform_func (G_TYPE_STRING, G_TYPE_UINT64, value_transform_uint64); - g_value_register_transform_func (G_TYPE_STRING, G_TYPE_LONG, value_transform_long); - g_value_register_transform_func (G_TYPE_STRING, G_TYPE_ULONG, value_transform_ulong); - g_value_register_transform_func (G_TYPE_STRING, G_TYPE_FLOAT, value_transform_float); - g_value_register_transform_func (G_TYPE_STRING, G_TYPE_DOUBLE, value_transform_double); - g_value_register_transform_func (G_TYPE_STRING, G_TYPE_BOOLEAN, value_transform_boolean); +static gboolean +consume_maybe (Environment *env, TokenType type) +{ + gboolean result; + GList *tmp; + + consume_spaces (env); + tmp = env->current; + result = env->current == NULL ? FALSE : (consume (env))->type == type; + + if (!result) + env->current = tmp; + + return result; +} + +static UfoTaskNode * read_connection (Environment *env, UfoTaskNode *previous); + +static gboolean +try_consume_assignment (Environment *env, UfoTaskNode *task) +{ + Token *key; + Token *equal; + Token *value; + + if (env->current == NULL) + return FALSE; + + key = consume (env); + + if (key->type != STRING) + return FALSE; + + equal = consume (env); - graph = UFO_TASK_GRAPH (ufo_task_graph_new ()); + if (equal == NULL || equal->type != ASSIGNMENT) + return FALSE; - /* push default group */ - g_queue_push_head (stack, NULL); + value = consume (env); - g_list_for (tokens, it) { - Token *t = (Token *) it->data; + if (value == NULL || key->type != STRING) + return FALSE; - if ((expected & t->type) == 0) { - g_print ("Error: cannot parse pipeline specification\n %s\n", pipeline); + set_property (task, key->str->str, value->str->str); - for (guint i = 0; i < t->pos + 2; i++) - g_print (" "); + return TRUE; +} + +static UfoTaskNode * +try_consume_task (Environment *env) +{ + Token *t; + UfoTaskNode *node = NULL; + + t = consume (env); - g_print ("^\n"); - return NULL; + if (t->type != STRING) + return NULL; + + node = ufo_plugin_manager_get_task (env->pm, t->str->str, env->error); + + if (node == NULL) + return NULL; + + consume_spaces (env); + + while (1) { + GList *tmp; + + tmp = env->current; + + if (!try_consume_assignment (env, node)) { + env->current = tmp; + break; } - switch (t->type) { - case PAREN_OPEN: - g_queue_push_head (stack, NULL); - expected = PAREN_OPEN | STRING; - break; - case PAREN_CLOSE: - group = g_queue_pop_head (stack); - expected = EXCLAMATION | SPACE; - break; - case EXCLAMATION: - action = NEW_TASK | CONNECT; - expected = STRING | SPACE; - break; - case STRING: - { - gchar *s = t->str->str; - - if (action & NEW_TASK) { - task = ufo_plugin_manager_get_task (pm, s, error); - - if (task == NULL) - return NULL; - - if (action & CONNECT) { - GList *jt; - guint i = 0; - - g_list_for (group, jt) - ufo_task_graph_connect_nodes_full (graph, UFO_TASK_NODE (jt->data), task, i++); - - /* create new group for this task */ - g_queue_pop_head (stack); - g_queue_push_head (stack, NULL); - } - - group = g_queue_pop_head (stack); - group = g_list_append (group, task); - g_queue_push_head (stack, group); - - action = PROP_KEY; - expected = STRING | PAREN_CLOSE | SPACE | COMMA; - } - else if (action & PROP_KEY) { - key = s; - expected = ASSIGNMENT; - } - else if (action & PROP_VALUE) { - set_property (task, key, s); - action = PROP_KEY; - expected = PAREN_CLOSE | COMMA | SPACE; - } + consume_spaces (env); + } + + return node; +} + +static GList * +read_params (Environment *env) +{ + /* + * params is something like "[A, B ! C, D]". The returned list should + * contain [A, C, D] which are the actual output streams for the subsequent + * task. + */ + GList *result = NULL; + + if (peek (env)->type != PAREN_OPEN) + return NULL; + + skip (env); + + while (1) { + UfoTaskNode *previous = NULL; + UfoTaskNode *last = NULL; + + if (peek (env)->type == PAREN_CLOSE) { + consume (env); + break; + } + + if (peek (env)->type == COMMA) { + consume (env); + continue; + } + + do { + last = previous; + previous = read_connection (env, previous); + } while (previous != NULL && *(env->error) == NULL); + + g_assert (UFO_IS_TASK_NODE (last)); + result = g_list_append (result, last); + } + + return result; +} + +static UfoTaskNode * +read_connection (Environment *env, UfoTaskNode *previous) +{ + UfoTaskNode *next; + GList *params = NULL; + + if (env->current == NULL) + return NULL; + + consume_spaces (env); + + if (peek (env)->type != PAREN_OPEN && peek (env)->type != STRING) + return NULL; + + params = read_params (env); + previous = try_consume_task (env); + + if (*(env->error) != NULL) + return NULL; + + do { + if (consume_maybe (env, EXCLAMATION)) { + consume_spaces (env); + next = try_consume_task (env); + + if (next == NULL) + return previous; + + if (params == NULL) { + ufo_task_graph_connect_nodes (env->graph, previous, next); + } + else { + GList *it; + guint i = 0; + g_list_for (params, it) { + UfoTaskNode *from; + + from = UFO_TASK_NODE (it->data); + ufo_task_graph_connect_nodes_full (env->graph, from, next, i++); } - break; - case COMMA: - action = NEW_TASK; - expected = STRING | SPACE; - break; - case ASSIGNMENT: - action = PROP_VALUE; - expected = STRING; - break; - case SPACE: - expected = STRING | SPACE | PAREN_OPEN | PAREN_CLOSE | EXCLAMATION; - break; + + g_list_free (params); + params = NULL; + } + + previous = next; + } + else { + next = NULL; } } + while (next != NULL); + + return previous; +} + +static UfoTaskGraph * +parse (const gchar *pipeline, GList *tokens, UfoPluginManager *pm, GError **error) +{ + UfoTaskNode *previous = NULL; + Environment env; + + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_UCHAR, value_transform_uchar); + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_INT, value_transform_int); + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_UINT, value_transform_uint); + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_UINT64, value_transform_uint64); + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_LONG, value_transform_long); + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_ULONG, value_transform_ulong); + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_FLOAT, value_transform_float); + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_DOUBLE, value_transform_double); + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_BOOLEAN, value_transform_boolean); + + env.current = tokens; + env.pm = pm; + env.graph = UFO_TASK_GRAPH (ufo_task_graph_new ()); + env.error = error; + + do { + previous = read_connection (&env, previous); + } while (previous != NULL && *(env.error) == NULL); - g_queue_free (stack); - return graph; + return (*env.error != NULL) ? NULL : env.graph; } static void |