Beispiel #1
0
def try_parse_text(content: Content) -> Optional[str]:
    c = content.peek()

    if c in TOKEN_CHARS:
        out = StringIO()

        while True:
            out.write(c)

            content.move_next()

            c = content.peek()

            if c is None or c not in TOKEN_CHARS:
                break

        return out.getvalue()
    elif c == TOKEN_DELIMITER_CHAR:
        content.move_next()

        out = StringIO()

        while True:
            c = content.peek()

            if c is None:
                raise Exception(f'Expected {TOKEN_DELIMITER_CHAR}')
            elif c == TOKEN_DELIMITER_CHAR:
                content.move_next()
                break
            elif c == TOKEN_ESCAPE_CHAR:
                content.move_next()

                c = content.peek()

                if c is None:
                    raise Exception('Expected escaped char.')
                elif c in [TOKEN_ESCAPE_CHAR, TOKEN_DELIMITER_CHAR]:
                    out.write(c)

                    content.move_next()
                else:
                    # TODO add unicode support

                    raise Exception(f'Invalid escaped char: {c}')
            else:
                content.move_next()

                out.write(c)

        return out.getvalue()

    return None
Beispiel #2
0
def parse_inline_link(ctx: CTX, mark: str, location: Location,
                      content: Content, indentation: int) -> int:
    if mark != link_text_begin_mark:
        return PASS

    with ctx.using_stop_mark(link_text_end_mark):
        # It uses the original indentation
        #   so the paragraph can be continued.
        contents = parse_inline(ctx, content.get_location(), content,
                                indentation)

    if not content.pull(link_text_end_mark):
        raise StxError(f'Expected mark: {link_text_end_mark}')

    if content.pull(link_ref_begin_mark):
        out = StringIO()

        while not content.pull(link_ref_end_mark):
            c = content.peek()

            if c is None:
                raise StxError(f'Expected {link_ref_end_mark}',
                               content.get_location())

            out.write(c)
            content.move_next()

        reference = out.getvalue()
    else:
        reference = None

    ctx.composer.add(LinkText(location, contents, reference))

    return CONSUMED
Beispiel #3
0
def skip_void(content: Content):
    while True:
        c = content.peek()

        if c is None:
            break
        elif c in WHITESPACE_CHARS:
            content.move_next()
        else:
            break
Beispiel #4
0
def try_parse_group(content: Content):
    c = content.peek()

    if c == GROUP_BEGIN_CHAR:
        content.move_next()

        skip_void(content)

        items = parse_values(content)

        skip_void(content)

        c = content.peek()

        if c != GROUP_END_CHAR:
            raise StxError('Expected group end char', content.get_location())

        content.move_next()

        return Group(items)

    return None
Beispiel #5
0
def try_parse_item(content: Content,
                   allow_entry_separator=True) -> Optional[Value]:
    c = content.peek()

    if c == EMPTY_CHAR:
        content.move_next()

        return Empty()

    group = try_parse_group(content)

    if group is not None:
        return group

    return try_parse_token_or_entry(content, allow_entry_separator)
Beispiel #6
0
def parse_values(content: Content) -> List[Value]:
    items = []

    can_be_none = True

    while True:
        loc0 = content.get_location()

        value = try_parse_item(content)

        if value is None:
            if can_be_none:
                content.go_back(loc0)
                break
            else:
                raise Exception('Expected to read a group item')

        items.append(value)

        loc0 = content.get_location()

        skip_void(content)

        c = content.peek()

        if c == GROUP_SEPARATOR_CHAR:
            content.move_next()

            skip_void(content)

            can_be_none = False
        else:
            content.go_back(loc0)
            break

    return items
Beispiel #7
0
def try_parse_token_or_entry(
        content: Content,
        allow_entry_separator=True) -> Union[Token, Entry, None]:
    text = try_parse_text(content)

    if text is None:
        return None

    loc0 = content.get_location()

    skip_void(content)

    c = content.peek()

    if c == ENTRY_SEPARATOR_CHAR and allow_entry_separator:
        content.move_next()

        skip_void(content)

        entry_name = text
        entry_value = try_parse_item(content, allow_entry_separator=False)

        if entry_value is None:
            raise StxError('Expected an entry value', content.get_location())

        return Entry(entry_name, entry_value)

    group = try_parse_group(content)

    if group is not None:
        return Entry(text, group)

    # go before skipping void
    content.go_back(loc0)

    return Token(text)
Beispiel #8
0
def parse_inline_text(ctx: CTX, mark: str, location: Location,
                      content: Content, indentation: int) -> int:
    if mark is not None:
        return PASS

    out = StringIO()

    completed = False

    while content.peek() is not None:
        # Check if the text is broken by an inline or stop mark
        if content.test_any(inline_marks):
            break
        elif content.test(ctx.stop_mark):
            break

        c = content.peek()

        if c == '\n':
            out.write(c)
            content.move_next()

            # Check if the text is completed by an empty line
            if content.consume_empty_line():
                completed = True
                break

            loc0 = content.get_location()

            spaces = content.read_spaces(indentation)

            # Check if the text is completed by indentation change
            if spaces < indentation:
                content.go_back(loc0)
                completed = True
                break

            # Check if the text is completed by a non-inline mark
            if content.test_any(not_inline_marks):
                content.go_back(loc0)
                completed = True
                break
        elif c == escape_char:
            content.move_next()

            escaped_mark = content.pull_any(all_marks)
            if escaped_mark is not None:
                out.write(escaped_mark)
            elif content.pull(ctx.stop_mark):
                out.write(ctx.stop_mark)
            elif content.pull(escape_char):
                out.write(escape_char)
            else:
                raise StxError('invalid escaped char')
        else:
            out.write(c)
            content.move_next()

    text = out.getvalue()

    if text == '':
        return EXIT

    ctx.composer.add(PlainText(location, text))

    if completed:
        return EXIT
    return CONSUMED
Beispiel #9
0
def parse_table(ctx: CTX, mark: str, location: Location,
                content: Content) -> int:
    if mark == header_row_block_mark:
        header = True
        reuse_row = False
    elif mark == normal_row_block_mark:
        header = False
        reuse_row = False
    elif mark == cell_block_mark:
        header = False
        reuse_row = True
    else:
        return PASS

    table = ctx.composer.get_last_component()

    if not isinstance(table, Table):
        table = Table(location)

        ctx.composer.add(table)

    row = table.get_last_row() if reuse_row else None

    if row is None:
        row = TableRow(location, header)

        table.rows.append(row)

    # TODO is this ok?
    skip_void(content)

    indentation0 = content.column
    indentation = indentation0

    while True:
        with ctx.using_stop_mark(cell_block_mark):
            cell = capture_component(ctx, indentation, True)

            row.cells.append(cell)

        if not ctx.reader.active():
            break

        content = ctx.reader.get_content()

        loc0 = content.get_location()

        # Consume indentation when it is the beginning of the line
        if content.column == 0:
            if content.read_spaces(indentation0) < indentation0:
                content.go_back(loc0)
                break

        if content.peek() == cell_block_mark:
            content.move_next()
            content.read_spaces()

            indentation = content.column
        else:
            break

    return CONSUMED