@RequiredKeys(rewriter_keys['target'])
def process_target(self, cmd):
    mlog.log('Processing target', mlog.bold(cmd['target']), 'operation', mlog.cyan(cmd['operation']))
    target = self.find_target(cmd['target'])
    if ((target is None) and (cmd['operation'] != 'target_add')):
        mlog.error('Unknown target', mlog.bold(cmd['target']), *self.on_error())
        return self.handle_error()

    def rel_source(src: str) -> str:
        subdir = os.path.abspath(os.path.join(self.sourcedir, target['subdir']))
        if os.path.isabs(src):
            return os.path.relpath(src, subdir)
        elif (not os.path.exists(src)):
            return src
        return os.path.relpath(os.path.abspath(src), subdir)
    if (target is not None):
        cmd['sources'] = [rel_source(x) for x in cmd['sources']]

    def arg_list_from_node(n):
        args = []
        if isinstance(n, FunctionNode):
            args = list(n.args.arguments)
            if (n.func_name in BUILD_TARGET_FUNCTIONS):
                args.pop(0)
        elif isinstance(n, ArrayNode):
            args = n.args.arguments
        elif isinstance(n, ArgumentNode):
            args = n.arguments
        return args
    to_sort_nodes = []
    if (cmd['operation'] == 'src_add'):
        node = None
        if target['sources']:
            node = target['sources'][0]
        else:
            node = target['node']
        assert (node is not None)
        src_list = []
        for i in target['sources']:
            for j in arg_list_from_node(i):
                if isinstance(j, StringNode):
                    src_list += [j.value]
        to_append = []
        for i in sorted(set(cmd['sources'])):
            if (i in src_list):
                mlog.log('  -- Source', mlog.green(i), 'is already defined for the target --> skipping')
                continue
            mlog.log('  -- Adding source', mlog.green(i), 'at', mlog.yellow(f'{node.filename}:{node.lineno}'))
            token = Token('string', node.filename, 0, 0, 0, None, i)
            to_append += [StringNode(token)]
        arg_node = None
        if isinstance(node, (FunctionNode, ArrayNode)):
            arg_node = node.args
        elif isinstance(node, ArgumentNode):
            arg_node = node
        assert (arg_node is not None)
        arg_node.arguments += to_append
        if ((arg_node not in to_sort_nodes) and (not isinstance(node, FunctionNode))):
            to_sort_nodes += [arg_node]
        if (node not in self.modified_nodes):
            self.modified_nodes += [node]
    elif (cmd['operation'] == 'src_rm'):

        def find_node(src):
            for i in target['sources']:
                for j in arg_list_from_node(i):
                    if isinstance(j, StringNode):
                        if (j.value == src):
                            return (i, j)
            return (None, None)
        for i in cmd['sources']:
            (root, string_node) = find_node(i)
            if (root is None):
                mlog.warning('  -- Unable to find source', mlog.green(i), 'in the target')
                continue
            arg_node = None
            if isinstance(root, (FunctionNode, ArrayNode)):
                arg_node = root.args
            elif isinstance(root, ArgumentNode):
                arg_node = root
            assert (arg_node is not None)
            mlog.log('  -- Removing source', mlog.green(i), 'from', mlog.yellow(f'{string_node.filename}:{string_node.lineno}'))
            arg_node.arguments.remove(string_node)
            if ((arg_node not in to_sort_nodes) and (not isinstance(root, FunctionNode))):
                to_sort_nodes += [arg_node]
            if (root not in self.modified_nodes):
                self.modified_nodes += [root]
    elif (cmd['operation'] == 'extra_files_add'):
        tgt_function: FunctionNode = target['node']
        mark_array = True
        try:
            node = target['extra_files'][0]
        except IndexError:
            try:
                extra_files_key = next((k for k in tgt_function.args.kwargs.keys() if (isinstance(k, IdNode) and (k.value == 'extra_files'))))
                node = tgt_function.args.kwargs[extra_files_key]
            except StopIteration:
                node = ArrayNode(ArgumentNode(Token('', tgt_function.filename, 0, 0, 0, None, '[]')), tgt_function.end_lineno, tgt_function.end_colno, tgt_function.end_lineno, tgt_function.end_colno)
                tgt_function.args.kwargs[IdNode(Token('string', tgt_function.filename, 0, 0, 0, None, 'extra_files'))] = node
                mark_array = False
                if (tgt_function not in self.modified_nodes):
                    self.modified_nodes += [tgt_function]
            target['extra_files'] = [node]
        if isinstance(node, IdNode):
            node = self.interpreter.assignments[node.value]
            target['extra_files'] = [node]
        if (not isinstance(node, ArrayNode)):
            mlog.error('Target', mlog.bold(cmd['target']), 'extra_files argument must be a list', *self.on_error())
            return self.handle_error()
        extra_files_list = []
        for i in target['extra_files']:
            for j in arg_list_from_node(i):
                if isinstance(j, StringNode):
                    extra_files_list += [j.value]
        to_append = []
        for i in sorted(set(cmd['sources'])):
            if (i in extra_files_list):
                mlog.log('  -- Extra file', mlog.green(i), 'is already defined for the target --> skipping')
                continue
            mlog.log('  -- Adding extra file', mlog.green(i), 'at', mlog.yellow(f'{node.filename}:{node.lineno}'))
            token = Token('string', node.filename, 0, 0, 0, None, i)
            to_append += [StringNode(token)]
        arg_node = node.args
        arg_node.arguments += to_append
        if (arg_node not in to_sort_nodes):
            to_sort_nodes += [arg_node]
        if (mark_array and (node not in self.modified_nodes)):
            self.modified_nodes += [node]
    elif (cmd['operation'] == 'extra_files_rm'):

        def find_node(src):
            for i in target['extra_files']:
                for j in arg_list_from_node(i):
                    if isinstance(j, StringNode):
                        if (j.value == src):
                            return (i, j)
            return (None, None)
        for i in cmd['sources']:
            (root, string_node) = find_node(i)
            if (root is None):
                mlog.warning('  -- Unable to find extra file', mlog.green(i), 'in the target')
                continue
            arg_node = root.args
            mlog.log('  -- Removing extra file', mlog.green(i), 'from', mlog.yellow(f'{string_node.filename}:{string_node.lineno}'))
            arg_node.arguments.remove(string_node)
            if ((arg_node not in to_sort_nodes) and (not isinstance(root, FunctionNode))):
                to_sort_nodes += [arg_node]
            if (root not in self.modified_nodes):
                self.modified_nodes += [root]
    elif (cmd['operation'] == 'target_add'):
        if (target is not None):
            mlog.error('Can not add target', mlog.bold(cmd['target']), 'because it already exists', *self.on_error())
            return self.handle_error()
        id_base = re.sub('[- ]', '_', cmd['target'])
        target_id = ((id_base + '_exe') if (cmd['target_type'] == 'executable') else '_lib')
        source_id = (id_base + '_sources')
        filename = os.path.join(cmd['subdir'], environment.build_filename)
        src_arg_node = ArgumentNode(Token('string', filename, 0, 0, 0, None, ''))
        src_arr_node = ArrayNode(src_arg_node, 0, 0, 0, 0)
        src_far_node = ArgumentNode(Token('string', filename, 0, 0, 0, None, ''))
        src_fun_node = FunctionNode(filename, 0, 0, 0, 0, 'files', src_far_node)
        src_ass_node = AssignmentNode(filename, 0, 0, source_id, src_fun_node)
        src_arg_node.arguments = [StringNode(Token('string', filename, 0, 0, 0, None, x)) for x in cmd['sources']]
        src_far_node.arguments = [src_arr_node]
        tgt_arg_node = ArgumentNode(Token('string', filename, 0, 0, 0, None, ''))
        tgt_fun_node = FunctionNode(filename, 0, 0, 0, 0, cmd['target_type'], tgt_arg_node)
        tgt_ass_node = AssignmentNode(filename, 0, 0, target_id, tgt_fun_node)
        tgt_arg_node.arguments = [StringNode(Token('string', filename, 0, 0, 0, None, cmd['target'])), IdNode(Token('string', filename, 0, 0, 0, None, source_id))]
        src_ass_node.accept(AstIndentationGenerator())
        tgt_ass_node.accept(AstIndentationGenerator())
        self.to_add_nodes += [src_ass_node, tgt_ass_node]
    elif (cmd['operation'] == 'target_rm'):
        to_remove = self.find_assignment_node(target['node'])
        if (to_remove is None):
            to_remove = target['node']
        self.to_remove_nodes += [to_remove]
        mlog.log('  -- Removing target', mlog.green(cmd['target']), 'at', mlog.yellow(f'{to_remove.filename}:{to_remove.lineno}'))
    elif (cmd['operation'] == 'info'):
        src_list = []
        for i in target['sources']:
            for j in arg_list_from_node(i):
                if isinstance(j, StringNode):
                    src_list += [j.value]
        extra_files_list = []
        for i in target['extra_files']:
            for j in arg_list_from_node(i):
                if isinstance(j, StringNode):
                    extra_files_list += [j.value]
        test_data = {'name': target['name'], 'sources': src_list, 'extra_files': extra_files_list}
        self.add_info('target', target['id'], test_data)
    for i in to_sort_nodes:
        convert = (lambda text: (int(text) if text.isdigit() else text.lower()))
        alphanum_key = (lambda key: [convert(c) for c in re.split('([0-9]+)', key)])
        path_sorter = (lambda key: [((key.count('/') <= idx), alphanum_key(x)) for (idx, x) in enumerate(key.split('/'))])
        unknown = [x for x in i.arguments if (not isinstance(x, StringNode))]
        sources = [x for x in i.arguments if isinstance(x, StringNode)]
        sources = sorted(sources, key=(lambda x: path_sorter(x.value)))
        i.arguments = (unknown + sources)
@RequiredKeys(rewriter_keys['target'])
def process_target(self, cmd):
    mlog.log('Processing target', mlog.bold(cmd['target']), 'operation', mlog.cyan(cmd['operation']))
    target = self.find_target(cmd['target'])
    if ((target is None) and (cmd['operation'] != 'target_add')):
        mlog.error('Unknown target', mlog.bold(cmd['target']), *self.on_error())
        return self.handle_error()

    def rel_source(src: str) -> str:
        subdir = os.path.abspath(os.path.join(self.sourcedir, target['subdir']))
        if os.path.isabs(src):
            return os.path.relpath(src, subdir)
        elif (not os.path.exists(src)):
            return src
        return os.path.relpath(os.path.abspath(src), subdir)
    if (target is not None):
        cmd['sources'] = [rel_source(x) for x in cmd['sources']]

    def arg_list_from_node(n):
        args = []
        if isinstance(n, FunctionNode):
            args = list(n.args.arguments)
            if (n.func_name in BUILD_TARGET_FUNCTIONS):
                args.pop(0)
        elif isinstance(n, ArrayNode):
            args = n.args.arguments
        elif isinstance(n, ArgumentNode):
            args = n.arguments
        return args
    to_sort_nodes = []
    if (cmd['operation'] == 'src_add'):
        node = None
        if target['sources']:
            node = target['sources'][0]
        else:
            node = target['node']
        assert (node is not None)
        src_list = []
        for i in target['sources']:
            for j in arg_list_from_node(i):
                if isinstance(j, StringNode):
                    src_list += [j.value]
        to_append = []
        for i in sorted(set(cmd['sources'])):
            if (i in src_list):
                mlog.log('  -- Source', mlog.green(i), 'is already defined for the target --> skipping')
                continue
            mlog.log('  -- Adding source', mlog.green(i), 'at', mlog.yellow(f'{node.filename}:{node.lineno}'))
            token = Token('string', node.filename, 0, 0, 0, None, i)
            to_append += [StringNode(token)]
        arg_node = None
        if isinstance(node, (FunctionNode, ArrayNode)):
            arg_node = node.args
        elif isinstance(node, ArgumentNode):
            arg_node = node
        assert (arg_node is not None)
        arg_node.arguments += to_append
        if ((arg_node not in to_sort_nodes) and (not isinstance(node, FunctionNode))):
            to_sort_nodes += [arg_node]
        if (node not in self.modified_nodes):
            self.modified_nodes += [node]
    elif (cmd['operation'] == 'src_rm'):

        def find_node(src):
            for i in target['sources']:
                for j in arg_list_from_node(i):
                    if isinstance(j, StringNode):
                        if (j.value == src):
                            return (i, j)
            return (None, None)
        for i in cmd['sources']:
            (root, string_node) = find_node(i)
            if (root is None):
                mlog.warning('  -- Unable to find source', mlog.green(i), 'in the target')
                continue
            arg_node = None
            if isinstance(root, (FunctionNode, ArrayNode)):
                arg_node = root.args
            elif isinstance(root, ArgumentNode):
                arg_node = root
            assert (arg_node is not None)
            mlog.log('  -- Removing source', mlog.green(i), 'from', mlog.yellow(f'{string_node.filename}:{string_node.lineno}'))
            arg_node.arguments.remove(string_node)
            if ((arg_node not in to_sort_nodes) and (not isinstance(root, FunctionNode))):
                to_sort_nodes += [arg_node]
            if (root not in self.modified_nodes):
                self.modified_nodes += [root]
    elif (cmd['operation'] == 'extra_files_add'):
        tgt_function: FunctionNode = target['node']
        mark_array = True
        try:
            node = target['extra_files'][0]
        except IndexError:
            try:
                extra_files_key = next((k for k in tgt_function.args.kwargs.keys() if (isinstance(k, IdNode) and (k.value == 'extra_files'))))
                node = tgt_function.args.kwargs[extra_files_key]
            except StopIteration:
                node = ArrayNode(ArgumentNode(Token(, tgt_function.filename, 0, 0, 0, None, '[]')), tgt_function.end_lineno, tgt_function.end_colno, tgt_function.end_lineno, tgt_function.end_colno)
                tgt_function.args.kwargs[IdNode(Token('string', tgt_function.filename, 0, 0, 0, None, 'extra_files'))] = node
                mark_array = False
                if (tgt_function not in self.modified_nodes):
                    self.modified_nodes += [tgt_function]
            target['extra_files'] = [node]
        if isinstance(node, IdNode):
            node = self.interpreter.assignments[node.value]
            target['extra_files'] = [node]
        if (not isinstance(node, ArrayNode)):
            mlog.error('Target', mlog.bold(cmd['target']), 'extra_files argument must be a list', *self.on_error())
            return self.handle_error()
        extra_files_list = []
        for i in target['extra_files']:
            for j in arg_list_from_node(i):
                if isinstance(j, StringNode):
                    extra_files_list += [j.value]
        to_append = []
        for i in sorted(set(cmd['sources'])):
            if (i in extra_files_list):
                mlog.log('  -- Extra file', mlog.green(i), 'is already defined for the target --> skipping')
                continue
            mlog.log('  -- Adding extra file', mlog.green(i), 'at', mlog.yellow(f'{node.filename}:{node.lineno}'))
            token = Token('string', node.filename, 0, 0, 0, None, i)
            to_append += [StringNode(token)]
        arg_node = node.args
        arg_node.arguments += to_append
        if (arg_node not in to_sort_nodes):
            to_sort_nodes += [arg_node]
        if (mark_array and (node not in self.modified_nodes)):
            self.modified_nodes += [node]
    elif (cmd['operation'] == 'extra_files_rm'):

        def find_node(src):
            for i in target['extra_files']:
                for j in arg_list_from_node(i):
                    if isinstance(j, StringNode):
                        if (j.value == src):
                            return (i, j)
            return (None, None)
        for i in cmd['sources']:
            (root, string_node) = find_node(i)
            if (root is None):
                mlog.warning('  -- Unable to find extra file', mlog.green(i), 'in the target')
                continue
            arg_node = root.args
            mlog.log('  -- Removing extra file', mlog.green(i), 'from', mlog.yellow(f'{string_node.filename}:{string_node.lineno}'))
            arg_node.arguments.remove(string_node)
            if ((arg_node not in to_sort_nodes) and (not isinstance(root, FunctionNode))):
                to_sort_nodes += [arg_node]
            if (root not in self.modified_nodes):
                self.modified_nodes += [root]
    elif (cmd['operation'] == 'target_add'):
        if (target is not None):
            mlog.error('Can not add target', mlog.bold(cmd['target']), 'because it already exists', *self.on_error())
            return self.handle_error()
        id_base = re.sub('[- ]', '_', cmd['target'])
        target_id = ((id_base + '_exe') if (cmd['target_type'] == 'executable') else '_lib')
        source_id = (id_base + '_sources')
        filename = os.path.join(cmd['subdir'], environment.build_filename)
        src_arg_node = ArgumentNode(Token('string', filename, 0, 0, 0, None, ))
        src_arr_node = ArrayNode(src_arg_node, 0, 0, 0, 0)
        src_far_node = ArgumentNode(Token('string', filename, 0, 0, 0, None, ))
        src_fun_node = FunctionNode(filename, 0, 0, 0, 0, 'files', src_far_node)
        src_ass_node = AssignmentNode(filename, 0, 0, source_id, src_fun_node)
        src_arg_node.arguments = [StringNode(Token('string', filename, 0, 0, 0, None, x)) for x in cmd['sources']]
        src_far_node.arguments = [src_arr_node]
        tgt_arg_node = ArgumentNode(Token('string', filename, 0, 0, 0, None, ))
        tgt_fun_node = FunctionNode(filename, 0, 0, 0, 0, cmd['target_type'], tgt_arg_node)
        tgt_ass_node = AssignmentNode(filename, 0, 0, target_id, tgt_fun_node)
        tgt_arg_node.arguments = [StringNode(Token('string', filename, 0, 0, 0, None, cmd['target'])), IdNode(Token('string', filename, 0, 0, 0, None, source_id))]
        src_ass_node.accept(AstIndentationGenerator())
        tgt_ass_node.accept(AstIndentationGenerator())
        self.to_add_nodes += [src_ass_node, tgt_ass_node]
    elif (cmd['operation'] == 'target_rm'):
        to_remove = self.find_assignment_node(target['node'])
        if (to_remove is None):
            to_remove = target['node']
        self.to_remove_nodes += [to_remove]
        mlog.log('  -- Removing target', mlog.green(cmd['target']), 'at', mlog.yellow(f'{to_remove.filename}:{to_remove.lineno}'))
    elif (cmd['operation'] == 'info'):
        src_list = []
        for i in target['sources']:
            for j in arg_list_from_node(i):
                if isinstance(j, StringNode):
                    src_list += [j.value]
        extra_files_list = []
        for i in target['extra_files']:
            for j in arg_list_from_node(i):
                if isinstance(j, StringNode):
                    extra_files_list += [j.value]
        test_data = {'name': target['name'], 'sources': src_list, 'extra_files': extra_files_list}
        self.add_info('target', target['id'], test_data)
    for i in to_sort_nodes:
        convert = (lambda text: (int(text) if text.isdigit() else text.lower()))
        alphanum_key = (lambda key: [convert(c) for c in re.split('([0-9]+)', key)])
        path_sorter = (lambda key: [((key.count('/') <= idx), alphanum_key(x)) for (idx, x) in enumerate(key.split('/'))])
        unknown = [x for x in i.arguments if (not isinstance(x, StringNode))]
        sources = [x for x in i.arguments if isinstance(x, StringNode)]
        sources = sorted(sources, key=(lambda x: path_sorter(x.value)))
        i.arguments = (unknown + sources)