Converts the specified block content lines (list of strings) into a Block object.

    Nested blocks are recursively converted into strings and cached in the block_map. Include tags
    are processed as well. The resulting block string is also added to the block_map, along with any
    nested blocks.

    PARAMETERS:
    lines       -- str; content lines to be converted into a Block.
    block_map   -- BlockMap
    link_stack  -- LinkStack
    source_path -- str; the path of the file from which this content came, relative to the current
                   working directory.
    block_name  -- str; the name of the block that contains this content. By default, the name is
                  'all'

    RETURNS:
    Block; the result of converting the block content.

def convert_lines_to_block(lines, block_map, link_stack, source_path, block_name=ALL_BLOCK_NAME):
    """Converts the specified block content lines (list of strings) into a Block object.

    Nested blocks are recursively converted into strings and cached in the block_map. Include tags
    are processed as well. The resulting block string is also added to the block_map, along with any
    nested blocks.

    PARAMETERS:
    lines       -- str; content lines to be converted into a Block.
    block_map   -- BlockMap
    link_stack  -- LinkStack
    source_path -- str; the path of the file from which this content came, relative to the current
                   working directory.
    block_name  -- str; the name of the block that contains this content. By default, the name is
                  'all'

    RETURNS:
    Block; the result of converting the block content.
    """
    found_closing_block = False
    segments = []
    while lines:
        line = lines.pop(0)

        # Check if the line is a closing tag. This should only occur if we encounter the closing
        # block tag that matches the block_name parameter.
        close_tag_match = BLOCK_CLOSE_REGEX.match(line)
        if close_tag_match:
            if close_tag_match.group(1) != block_name:
                raise InvalidBlockName('Expected closing block ' + block_name + \
                    'but found block named "' + block_tag_match.group(1) + '" in ' + source_path)
            # If the block name is valid, we are done processing this block.
            found_closing_block = True
            break

        # Otherwise, check if the line is a nested block.
        open_tag_match = BLOCK_OPEN_REGEX.match(line)
        if open_tag_match:
            # Make sure the block name is not the reserved ALL_BLOCK_NAME.
            inner_block_name = open_tag_match.group(1)
            if inner_block_name == ALL_BLOCK_NAME:
                raise InvalidBlockName(
                    '"{0}" is a reserved block name, but found block named "{0}" in {1}'.format(
                        ALL_BLOCK_NAME, source_path))

            # Recursively convert nested block contents to a Block.
            inner_block = convert_lines_to_block(
                lines,
                block_map,
                link_stack,
                source_path,
                inner_block_name)
            segments.append(inner_block)
            continue

        # Otherwise, check if the line is a variable. The line should be omitted.
        variable_match = VARIABLE_REGEX.match(line)
        if variable_match:
            process_variable(variable_match, block_map)
            continue

        # Otherwise, check if the line is an include tag.
        include_match = INCLUDE_REGEX.match(line)
        if include_match:
            included_content = process_links(include_match, block_map, link_stack, source_path)
            # Omit empty content.
            if included_content != '':
                append_text_to_segments(segments, included_content)
        else:
            append_text_to_segments(segments, line)

    if block_name != ALL_BLOCK_NAME and not found_closing_block:
        raise InvalidBlockName(
                'Expected closing block called "{0}" in {1}'.format(block_name, source_path))

    block = Block(source_path, block_name, segments)
    block_map.add_block(block)
    return block