Exemple #1
0
def parsed_tree_to_formula(parsed_tree):
    """Build a formula object from the result of parsing."""
    if parsed_tree[0].name == "START" or parsed_tree[0].name == "r_expr":
        rep = parsed_tree_to_formula(parsed_tree[1])
    elif parsed_tree[0].name == "r_mu":
        variable_name = parsed_tree[3][1]
        phi = parsed_tree_to_formula(parsed_tree[5])
        rep = Mu(variable_name, phi)
    elif parsed_tree[0].name == "r_or":
        sub_formulas = parsed_tree[3::2]
        rep = Or([parsed_tree_to_formula(f) for f in sub_formulas])
    elif parsed_tree[0].name == "r_var":
        variable_name = parsed_tree[3][1]
        rep = Var(variable_name)
    elif parsed_tree[0].name == "r_cnt":
        variable_name = parsed_tree[3][1]
        rep = Cnt(variable_name)
    elif parsed_tree[0].name == "r_next":
        relation_name = parsed_tree[2][1]
        sub_formula = parsed_tree_to_formula(parsed_tree[4])
        rep = Geq(1, relation_name, sub_formula)
    elif parsed_tree[0].name == "r_geq":
        lower_bound = parsed_tree[2][1]
        relation_name = parsed_tree[4][1]
        sub_formula = parsed_tree_to_formula(parsed_tree[6])
        rep = Geq(int(lower_bound), relation_name, sub_formula)
    elif parsed_tree[0].name == "r_not":
        sub_formula = parsed_tree_to_formula(parsed_tree[3])
        rep = Not(sub_formula)
    else:
        raise ParsingError("Parsing of formula failed")

    return rep
Exemple #2
0
def make_canonical_commands(g, commands, di=False):
    """ Takes commands and the graph it refers to and returns a list of
        canonical transformations that have the same behaviour.
        The canonical form of a transformation follows this pattern :
            DELETIONS (DELETE_NODE, DELETE_NODE_ATTRS, DELETE_EDGE, DELETE_EDGE_ATTRS)
            CLONING (CLONE)
            ADDING and MERGING (ADD_NODE, ADD_NODE_ATTRS, ADD_EDGE, ADD_EDGE_ATTRS, MERGE)
    """
    res = []

    # We do multiple steps of simplification, until we found a fixed-point

    aux = commands
    next_step = simplify_commands(commands, di)
    while next_step != aux:
        aux = next_step
        next_step = simplify_commands(aux, di)

    # We keep updated an environment with our nodes and our edges

    env_nodes = [n for n in g.nodes()]
    env_edges = [e for e in g.edges()]

    if not di:
        for e in g.edges():
            if not (e[1], e[0]) in env_edges:
                env_edges.append((e[1], e[0]))

    # For each transformation we choose if we do it in this step or if we
    # keep it for later

    while next_step != '':
        command_strings = [c for c in next_step.splitlines() if len(c) > 0]
        actions = []
        for command in command_strings:
            try:
                parsed = parser.parseString(command).asDict()
                actions.append(parsed)
            except:
                raise ParsingError("Cannot parse command '%s'" % command)

        next_step = ''

        # We have 3 strings for each line of the canonical pattern

        add_step = ''
        del_step = ''
        clone_step = ''

        # Added is the list of elements we will add at to our environment
        # at the end of the step, we add them at the end so they are not
        # taken into account in the current step

        added = []
        cloned = []

        # If a node is in clone_wait, every cloning operation on it will
        # be delayed to next step. Same for other lists

        clone_wait = []
        merge_wait = []
        del_wait = []
        ad_wait = []

        # If we can't add a node with name n in this step, we don't want
        # another node with the same name to be added before it

        protected_names = []

        # For each action we update our lists and we chose what to do

        for i in range(len(actions)):
            action = actions[i]
            if action["keyword"] == "add_node":
                if action["node"] not in protected_names:
                    add_step += command_strings[i] + "\n"
                    added.append(action["node"])
            elif action["keyword"] == "delete_node":
                if action["node"] in env_nodes and\
                   action["node"] not in del_wait:
                    del_step += command_strings[i] + "\n"
                    env_nodes.remove(action["node"])
                else:
                    next_step += command_strings[i] + "\n"
                    ad_wait.append(action["node"])
            elif action["keyword"] == "add_node_attrs":
                if action["node"] in env_nodes and\
                   action["node"] not in ad_wait:
                    add_step += command_strings[i] + "\n"
                    added.append(action["node"])
                    clone_wait.append(action["node"])
                else:
                    next_step += command_strings[i] + "\n"
                    ad_wait.append(action["node"])
                    clone_wait.append(action["node"])
            elif action["keyword"] == "delete_node_attrs":
                if action["node"] in env_nodes and\
                   action["node"] not in del_wait:
                    del_step += command_strings[i] + "\n"
                else:
                    next_step += command_strings[i] + "\n"
                    clone_wait.append(action["node"])
                    ad_wait.append(action["node"])
            elif action["keyword"] == "add_edge":
                e = (action["node_1"], action["node_2"])
                if e[0] in env_nodes and\
                   e[1] in env_nodes and\
                   e[0] not in ad_wait and\
                   e[1] not in ad_wait:
                    add_step += command_strings[i] + "\n"
                    added.append(e)
                    if not di:
                        added.append((e[1], e[0]))
                    clone_wait.append(action["node_1"])
                    clone_wait.append(action["node_2"])
                else:
                    next_step += command_strings[i] + "\n"
                    clone_wait.append(action["node_1"])
                    clone_wait.append(action["node_2"])
                    merge_wait.append(action["node_1"])
                    merge_wait.append(action["node_2"])
            elif action["keyword"] == "delete_edge":
                e = (action["node_1"], action["node_2"])
                if (e in env_edges or
                    (not di and (e[1], e[0]) in env_edges)) and\
                   e[0] not in del_wait and\
                   e[1] not in del_wait:
                    is_cloned = False
                    for l in cloned:
                        if e[0] in l:
                            next_step += command_strings[i] + "\n"
                            clone_wait.append(action["node_1"])
                            clone_wait.append(action["node_2"])
                            merge_wait.append(action["node_1"])
                            merge_wait.append(action["node_2"])
                            is_cloned = True
                            break
                    if not is_cloned:
                        del_step += command_strings[i] + "\n"
                        clone_wait.append(action["node_1"])
                        clone_wait.append(action["node_2"])
                        env_edges.remove(e)
                        if not di:
                            env_edges.remove((e[1], e[0]))
                else:
                    next_step += command_strings[i] + "\n"
                    clone_wait.append(action["node_1"])
                    clone_wait.append(action["node_2"])
                    merge_wait.append(action["node_1"])
                    merge_wait.append(action["node_2"])
            elif action["keyword"] == "add_edge_attrs":
                e = (action["node_1"], action["node_2"])
                if (e in env_edges or
                    (not di and (e[1], e[0]) in env_edges)) and\
                   e[0] not in ad_wait and\
                   e[1] not in ad_wait:
                    add_step += command_strings[i] + "\n"
                    added.append(e)
                    if not di:
                        added.append((e[1], e[0]))
                    clone_wait.append(action["node_1"])
                    clone_wait.append(action["node_2"])
                else:
                    next_step += command_strings[i] + "\n"
                    clone_wait.append(action["node_1"])
                    clone_wait.append(action["node_2"])
                    merge_wait.append(action["node_1"])
                    merge_wait.append(action["node_2"])
            elif action["keyword"] == "delete_edge_attrs":
                e = (action["node_1"], action["node_2"])
                if (e in env_edges or
                    (not di and (e[1], e[0]) in env_edges)) and\
                   e[0] not in del_wait and\
                   e[1] not in del_wait:
                    is_cloned = False
                    for l in cloned:
                        if e[0] in l:
                            next_step += command_strings[i] + "\n"
                            clone_wait.append(action["node_1"])
                            clone_wait.append(action["node_2"])
                            merge_wait.append(action["node_1"])
                            merge_wait.append(action["node_2"])
                            is_cloned = True
                        elif e[1] in l:
                            next_step += command_strings[i] + "\n"
                            clone_wait.append(action["node_1"])
                            clone_wait.append(action["node_2"])
                            merge_wait.append(action["node_1"])
                            merge_wait.append(action["node_2"])
                            is_cloned = True
                    if not is_cloned:
                        del_step += command_strings[i] + "\n"
                        clone_wait.append(action["node_1"])
                        clone_wait.append(action["node_2"])
                else:
                    next_step += command_strings[i] + "\n"
                    clone_wait.append(action["node_1"])
                    clone_wait.append(action["node_2"])
                    merge_wait.append(action["node_1"])
                    merge_wait.append(action["node_2"])
            elif action["keyword"] == "clone":
                node = action["node"]
                if "node_name" in action.keys():
                    new_node = action["node_name"]
                else:
                    j = 1
                    new_node = str(node) + str(j)
                    while new_node in env_nodes or new_node in added:
                        j += 1
                        new_node = str(node) + str(j)
                if node in env_nodes and\
                   node not in clone_wait and\
                   new_node not in protected_names and\
                   fold_left(lambda e, acc: (e != node or
                                             (type(e) == tuple and
                                              e[1] != node and
                                                 e[0] != node)) and
                             acc,
                             True,
                             added):
                    clone_step += command_strings[i] + "\n"
                    added.append(new_node)
                    del_wait.append(node)
                    found = False
                    for i in range(len(cloned)):
                        if node in cloned[i]:
                            cloned[i].append(new_node)
                            found = True
                    if not found:
                        cloned.append([new_node, node])
                    to_add = []
                    for e in env_edges:
                        if e[0] == node:
                            to_add.append((new_node, e[1]))
                        elif e[1] == node:
                            to_add.append((e[0], new_node))
                    for e in added:
                        if type(e) == tuple:
                            if e[0] == node and\
                               e[1] != node:
                                to_add.append((new_node, e[1]))
                            elif e[1] == node and e[0] != node:
                                to_add.append((e[0], new_node))
                    for e in to_add:
                        added.append(e)
                else:
                    next_step += command_strings[i] + "\n"
                    del_wait.append(node)
                    merge_wait.append(node)
                    ad_wait.append(node)
                    protected_names.append(new_node)
            elif action["keyword"] == "merge":
                if "node_name" in actions[i].keys():
                    node_name = actions[i]["node_name"]
                else:
                    node_name = "_".join(actions[i]["nodes"])
                if fold_left(lambda n, acc: (n in env_nodes and
                                             n not in merge_wait) and
                             acc,
                             True,
                             action["nodes"]) and\
                        node_name not in protected_names:
                    add_step += command_strings[i] + "\n"

                    added.append(node_name)
                    clone_wait.append(node_name)

                    rem_el = []
                    for e in env_edges:
                        if e[0] in action["nodes"] and\
                           e[1] in action["nodes"]:
                            if not e in rem_el:
                                rem_el.append(e)
                        if e[0] in action["nodes"]:
                            if not e in rem_el:
                                rem_el.append(e)
                            if e[1] not in action["nodes"]:
                                added.append((node_name, e[1]))
                        elif e[1] in action["nodes"]:
                            if not e in rem_el:
                                rem_el.append(e)
                            if e[0] not in action["nodes"]:
                                added.append((e[0], node_name))
                    for e in rem_el:
                        while e in env_edges:
                            env_edges.remove(e)
                            if not di:
                                env_edges.remove((e[1], e[0]))
                    rem_el = []
                    for e in added:
                        if type(e) == tuple:
                            if e[0] in action["nodes"] and\
                               e[1] in action["nodes"]:
                                if e not in rem_el:
                                    rem_el.append(e)
                            if e[0] in action["nodes"]:
                                if e not in rem_el:
                                    rem_el.append(e)
                                if e[1] not in action["nodes"]:
                                    added.append((node_name, e[1]))
                            elif e[1] in action["nodes"]:
                                if e not in rem_el:
                                    rem_el.append(e)
                                if e[0] not in action["nodes"]:
                                    added.append((e[0], node_name))
                    for e in rem_el:
                        while e in added:
                            added.remove(e)
                            if not di:
                                added.remove((e[1], e[0]))
                else:
                    next_step += command_strings[i] + "\n"
                    protected_names.append(node_name)

        for el in added:
            if type(el) == tuple:
                env_edges.append(el)
            else:
                env_nodes.append(el)

        if del_step + clone_step + add_step == '':
            raise ReGraphError(
                "Can't find any new transformations and actions is non-empty :\n%s"
                % next_step)

        res.append(del_step + clone_step + add_step)

    return res
Exemple #3
0
def simplify_commands(commands, di=False):
    """Simplify a list of graph transformation commands."""
    command_strings = [c for c in commands.splitlines() if len(c) > 0]
    actions = []
    for command in command_strings:
        try:
            parsed = parser.parseString(command).asDict()
            actions.append(parsed)
        except:
            raise ParsingError("Cannot parse command '%s'" % command)

    # We keep updated a list of the element we added, the lines of
    # transformations that added them or added attributes to them
    # and the type of addition we did (node or edge)

    added = []
    ad_index = []
    ad_type = []

    # We keep updated a list of the element we deleted and the lines of
    # transformation that deleted them or deleted attributes from them

    deleted = []
    del_index = []

    # We keep updated a list of the element we cloned and the line of
    # transformation that cloned them

    cloned = []
    clone_index = []

    # List of elements to remove at the end

    elements_to_remove = []

    # For each line of command we change what to remove and what to keep
    # We update the lists at each step, the only operations that actually
    # do simplify the commands are the deletion of nodes and edges and the
    # merges. They try to find the all the operations they can remove
    # without changing the behaviour

    for i in range(len(actions)):
        action = actions[i]
        if action["keyword"] == "add_node":
            added.append(action["node"])
            ad_index.append([i])
            ad_type.append("node")
        elif action["keyword"] == "delete_node":
            if action["node"] not in cloned:
                # If the node haven't been cloned before
                rem_el = []
                for j in range(len(added)):
                    el = added[j]
                    if (type(el) == tuple and (el[0] == action["node"] or
                                               el[1] == action["node"])) or\
                       el == action["node"]:
                        # If the node have been involved in an addition
                        # we remove that addition since it has been
                        # deleted now, if there are not more lines that
                        # refers to the addition of that node, we can
                        # remove the deletion of the node
                        # Finding the node in added is not enough to
                        # remove the deletion since it can be an
                        # addition of an edge, we have to check if it
                        # the node itself that we added
                        if el == action["node"]:
                            elements_to_remove.append(i)
                        for k in ad_index[j]:
                            elements_to_remove.append(k)
                        rem_el.append(j)
                k = 0
                for j in rem_el:
                    del added[j - k]
                    del ad_index[j - k]
                    del ad_type[j - k]
                    k += 1
                rem_el = []
                for j in range(len(deleted)):
                    el = deleted[j]
                    if (type(el) == tuple and (el[0] == action["node"] or
                                               el[1] == action["node"])) or\
                       el == action["node"]:
                        # If the node have been involved in a deletion
                        # we can remove that deletion since the deletion
                        # of the node itself will delete what the deletion
                        # would have deleted
                        for k in del_index[j]:
                            elements_to_remove.append(k)
                        rem_el.append(j)
                k = 0
                for j in rem_el:
                    del deleted[j - k]
                    del del_index[j - k]
                    k += 1
            else:
                # If the node have been cloned before, we can't delete the
                # transformations that happened before the cloning since
                # they affected the clones too. We do so by comparing the
                # line of the transformation we are looking at and the line
                # of the last cloning operation that happened
                rem_el = []
                ind = max([
                    clone_index[i] for i in range(len(cloned))
                    if cloned[i] == action["node"]
                ])
                for j in range(len(added)):
                    el = added[j]
                    if (type(el) == tuple and (el[0] == action["node"] or
                                               el[1] == action["node"])) or\
                            el == action["node"]:
                        rem_ind = []
                        for k in ad_index[j]:
                            if k > ind:
                                elements_to_remove.append(k)
                                rem_ind.append(k)
                        if ad_index[j] == rem_ind:
                            rem_el.append(j)
                        else:
                            for k in rem_ind:
                                ad_index[j].remove(k)
                m = 0
                for j in rem_el:
                    del added[j - m]
                    del ad_index[j - m]
                    del ad_type[j - m]
                    m += 1
                rem_el = []
                for j in range(len(deleted)):
                    el = deleted[j]
                    if (type(el) == tuple and (el[0] == action["node"] or
                                               el[1] == action["node"])) or\
                            el == action["node"]:
                        rem_ind = []
                        for k in del_index[j]:
                            if k > ind:
                                elements_to_remove.append(k)
                                rem_ind.append(k)
                        if del_index[j] == rem_ind:
                            rem_el.append(j)
                        else:
                            for k in rem_ind:
                                del_index[j].remove(k)
                m = 0
                for j in rem_el:
                    del deleted[j - m]
                    del del_index[j - m]
                    m += 1
                ind = clone_index.index(ind)
                del cloned[ind]
                del clone_index[ind]
            deleted.append(action["node"])
            del_index.append([i])
        elif action["keyword"] == "add_node_attrs":
            if action["node"] in added:
                j = added.index(action["node"])
                ad_index[j].append(i)
            else:
                added.append(action["node"])
                ad_index.append([i])
                ad_type.append("node_attrs")
        elif action["keyword"] == "delete_node_attrs":
            if action["node"] in deleted:
                j = deleted.index(action["node"])
                del_index[j].append(i)
            else:
                deleted.append(action["node"])
                del_index.append([i])
        elif action["keyword"] == "add_edge":
            e = (action["node_1"], action["node_2"])
            added.append(e)
            ad_index.append([i])
            ad_type.append("edge")
        elif action["keyword"] == "delete_edge":
            # It is the same idea as in the delete_node function, but with
            # a little bit more complexity since we have two nodes that
            # can possibly be cloned.
            # This time, finding the edge in the added list automatically
            # means we have to remove the deletion and the addition in the
            # case we didn't clone any of our nodes
            e = (action["node_1"], action["node_2"])
            if e[0] not in cloned and e[1] not in cloned:
                rem_el = []
                for j in range(len(added)):
                    el = added[j]
                    if type(el) == tuple and\
                       (el == e or (not di and el == (e[1], e[0]))):
                        elements_to_remove.append(i)
                        for k in ad_index[j]:
                            elements_to_remove.append(k)
                        rem_el.append(j)
                k = 0
                for j in rem_el:
                    del added[j - k]
                    del ad_index[j - k]
                    del ad_type[j - k]
                    k += 1
                rem_el = []
                for j in range(len(deleted)):
                    el = deleted[j]
                    if type(el) == tuple and\
                       (el == e or (not di and el == (e[1], e[0]))):
                        for k in del_index[j]:
                            elements_to_remove.append(k)
                        rem_el.append(j)
                k = 0
                for j in rem_el:
                    del deleted[j - k]
                    del del_index[j - k]
                    k += 1
            else:
                # Same idea as before if one of the nodes have been cloned,
                # but we have to take the max of the line number of all the
                # cloning operation on node 0 and node 1
                ind = 0
                if e[0] in cloned:
                    ind = max([
                        clone_index[i] for i in range(len(cloned))
                        if cloned[i] == e[0]
                    ])
                if e[1] in cloned:
                    ind = max([ind] + [
                        clone_index[i]
                        for i in range(len(cloned)) if cloned[i] == e[1]
                    ])

                ind = clone_index.index(ind)

                if e[0] in cloned:
                    rem_el = []
                    for j in range(len(added)):
                        el = added[j]
                        if type(el) == tuple and\
                           (el == e or (not di and el == (e[1], e[0]))):
                            rem_ind = []
                            for k in ad_index[j]:
                                if k > clone_index[ind]:
                                    elements_to_remove.append(k)
                                    # We remove the delete_edge operation
                                    # iff the same edge have been added
                                    # after the last cloning operation
                                    if ad_type[j] == "edge":
                                        elements_to_remove.append(i)
                                    rem_ind.append(k)
                            if ad_index[j] == rem_ind:
                                rem_el.append(j)
                            else:
                                for k in rem_ind:
                                    ad_index[j].remove(k)
                    m = 0
                    for j in rem_el:
                        del added[j - m]
                        del ad_index[j - m]
                        del ad_type[j - m]
                        m += 1
                    rem_el = []
                    for j in range(len(deleted)):
                        el = deleted[j]
                        if type(el) == tuple and\
                           (el == e or (not di and el == (e[1], e[0]))):
                            rem_ind = []
                            for k in del_index[j]:
                                if k > clone_index[ind]:
                                    elements_to_remove.append(k)
                                    rem_ind.append(k)
                            if del_index[j] == rem_ind:
                                rem_el.append(j)
                            else:
                                for k in rem_ind:
                                    del_index[j].remove(k)
                    m = 0
                    for j in rem_el:
                        del deleted[j - m]
                        del del_index[j - m]
                        m += 1
                if e[1] in cloned:
                    rem_el = []
                    for j in range(len(added)):
                        el = added[j]
                        if type(el) == tuple and\
                           (el == e or (not di and el == (e[1], e[0]))):
                            rem_ind = []
                            for k in ad_index[j]:
                                if k > clone_index[ind]:
                                    elements_to_remove.append(k)
                                    if ad_type[j] == "edge":
                                        elements_to_remove.append(i)
                                    rem_ind.append(k)
                            if ad_index[j] == rem_ind:
                                rem_el.append(j)
                            else:
                                for k in rem_ind:
                                    ad_index[j].remove(k)
                    m = 0
                    for j in rem_el:
                        del added[j - m]
                        del ad_index[j - m]
                        del ad_type[j - m]
                        m += 1
                    rem_el = []
                    for j in range(len(deleted)):
                        el = deleted[j]
                        if type(el) == tuple and\
                           (el == e or (not di and el == (e[1], e[0]))):
                            rem_ind = []
                            for k in del_index[j]:
                                if k > clone_index[ind]:
                                    elements_to_remove.append(k)
                                    rem_ind.append(k)
                            if del_index[j] == rem_ind:
                                rem_el.append(j)
                            else:
                                for k in rem_ind:
                                    del_index[j].remove(k)
                    m = 0
                    for j in rem_el:
                        del deleted[j - m]
                        del del_index[j - m]
                        m += 1
            deleted.append(e)
            del_index.append([i])
        elif action["keyword"] == "add_edge_attrs":
            e = (action["node_1"], action["node_2"])
            if e in added:
                j = added.index(e)
                ad_index[j].append(i)
            elif not di and (e[1], e[0]) in added:
                j = added.index((e[1], e[0]))
                ad_index[j].append(i)
            else:
                added.append(e)
                ad_index.append([i])
                ad_type.append("edge_attrs")
        elif action["keyword"] == "delete_edge_attrs":
            e = (action["node_1"], action["node_2"])
            if e in deleted:
                j = deleted.index(e)
                del_index[j].append(i)
            elif not di and (e[1], e[0]) in deleted:
                j = deleted.index((e[1], e[0]))
                del_index[j].append(i)
            else:
                deleted.append(e)
                del_index.append([i])
        elif action["keyword"] == "clone":
            if "node_name" in action.keys():
                added.append(action["node_name"])
                ad_index.append([i])
                ad_type.append("node")
            cloned.append(action["node"])
            clone_index.append(i)
        elif action["keyword"] == "merge":
            if "node_name" in action.keys():
                node_name = action["node_name"]
            else:
                node_name = "_".join(action["nodes"])

            added.append(node_name)
            ad_index.append([i])
            ad_type.append("node")

    return "\n".join([
        command_strings[i] for i in range(len(actions))
        if i not in elements_to_remove
    ])
Exemple #4
0
    def from_transform(cls, pattern, commands=None):
        """Initialize a rule from the transformation.

        On input takes a pattern which is used as `lhs` of the rule,
        as an optional argument transformation commands can be provided,
        by default the list of commands is empty and all `p`, `lhs` and `rhs`
        are initialized to be the same graph (pattern), later on
        when transformations are applied `p` and `rhs` are being updated.
        If list of commands is specified, these commands are simplified,
        transformed to the canonical order, and applied to `p`, `lhs` and `rhs`.

        Parameters
        ----------
        pattern : networkx.(Di)Graph
            Pattern graph to initialize and the lhs of the rule.
        commands : str, optional
            Script containing transformation commands, which
            can be parsed by `regraph.parser.parse`.

        """
        p = copy.deepcopy(pattern)
        lhs = copy.deepcopy(pattern)
        rhs = copy.deepcopy(pattern)
        p_lhs = dict([(n, n) for n in pattern.nodes()])
        p_rhs = dict([(n, n) for n in pattern.nodes()])

        rule = cls(p, lhs, rhs, p_lhs, p_rhs)

        # if the commands are provided, perform respecitive transformations
        if commands:
            # 1. make the commands canonical
            commands = make_canonical_commands(p, commands, p.is_directed())
            # 2. apply the commands

            command_strings = [
                c for b in commands if len(b) > 0 for c in b.splitlines()
            ]

            actions = []
            for command in command_strings:
                try:
                    parsed = parser.parseString(command).asDict()
                    actions.append(parsed)
                except:
                    raise ParsingError("Cannot parse command '%s'" % command)

            for action in actions:
                if action["keyword"] == "clone":
                    node_name = None
                    if "node_name" in action.keys():
                        node_name = action["node_name"]
                    rule.clone_node(action["node"], node_name)
                elif action["keyword"] == "merge":
                    node_name = None
                    if "node_name" in action.keys():
                        node_name = action["node_name"]
                    merged_node = rule.merge_node_list(
                        action["nodes"],
                        node_name)
                elif action["keyword"] == "add_node":
                    name = None
                    attrs = {}
                    if "node" in action.keys():
                        name = action["node"]
                    if "attributes" in action.keys():
                        attrs = action["attributes"]
                    rule.add_node(name, attrs)
                elif action["keyword"] == "delete_node":
                    rule.remove_node(action["node"])
                elif action["keyword"] == "add_edge":
                    attrs = {}
                    if "attributes" in action.keys():
                        attrs = action["attributes"]
                    rule.add_edge_rhs(
                        action["node_1"],
                        action["node_2"],
                        attrs)
                elif action["keyword"] == "delete_edge":
                    rule.remove_edge(
                        action["node_1"],
                        action["node_2"])
                elif action["keyword"] == "add_node_attrs":
                    rule.add_node_attrs(
                        action["node"],
                        action["attributes"])
                elif action["keyword"] == "add_edge_attrs":
                    rule.add_edge_attrs(
                        action["node_1"],
                        action["node_2"],
                        action["attributes"])
                elif action["keyword"] == "delete_node_attrs":
                    rule.remove_node_attrs(
                        action["node"],
                        action["attributes"])
                elif action["keyword"] == "delete_edge_attrs":
                    rule.remove_edge_attrs(
                        action["node_1"],
                        action["node_2"],
                        action["attributes"])
                else:
                    raise ParsingError("Unknown command %s" %
                                       action["keyword"])
        return rule