Exemplo n.º 1
0
def convert_subscriptlist(config: ParserConfig,
                          children: typing.Sequence[typing.Any]) -> typing.Any:
    if len(children) > 1:
        # This is a list of ExtSlice, so construct as such by grouping every
        # subscript with an optional comma and adding to a list.
        extslices = []
        for slice, comma in grouper(children, 2):
            if comma is None:
                extslices.append(ExtSlice(slice=slice.value))
            else:
                extslices.append(
                    ExtSlice(
                        slice=slice.value,
                        comma=Comma(
                            whitespace_before=parse_parenthesizable_whitespace(
                                config, comma.whitespace_before),
                            whitespace_after=parse_parenthesizable_whitespace(
                                config, comma.whitespace_after),
                        ),
                    ))
        return WithLeadingWhitespace(extslices, children[0].whitespace_before)
    else:
        # This is an Index or Slice, as parsed in the child.
        (index_or_slice, ) = children
        return index_or_slice
Exemplo n.º 2
0
def convert_with_stmt(config: ParserConfig, children: Sequence[Any]) -> Any:
    (with_token, *items, colon_token, suite) = children
    item_nodes: List[WithItem] = []

    for with_item, maybe_comma in grouper(items, 2):
        if maybe_comma is not None:
            item_nodes.append(
                with_item.with_changes(
                    comma=Comma(
                        whitespace_before=parse_parenthesizable_whitespace(
                            config, maybe_comma.whitespace_before
                        ),
                        whitespace_after=parse_parenthesizable_whitespace(
                            config, maybe_comma.whitespace_after
                        ),
                    )
                )
            )
        else:
            item_nodes.append(with_item)

    return WithLeadingWhitespace(
        With(
            whitespace_after_with=parse_simple_whitespace(
                config, with_token.whitespace_after
            ),
            items=tuple(item_nodes),
            whitespace_before_colon=parse_simple_whitespace(
                config, colon_token.whitespace_before
            ),
            body=suite,
        ),
        with_token.whitespace_before,
    )
Exemplo n.º 3
0
def convert_simple_stmt_partial(config: ParserConfig, children: Sequence[Any]) -> Any:
    *statements, trailing_whitespace = children

    last_stmt = len(statements) / 2
    body = []
    for i, (stmt_body, semi) in enumerate(grouper(statements, 2)):
        if semi is not None:
            if i == (last_stmt - 1):
                # Trailing semicolons only own the whitespace before.
                semi = Semicolon(
                    whitespace_before=parse_simple_whitespace(
                        config, semi.whitespace_before
                    ),
                    whitespace_after=SimpleWhitespace(""),
                )
            else:
                # Middle semicolons own the whitespace before and after.
                semi = Semicolon(
                    whitespace_before=parse_simple_whitespace(
                        config, semi.whitespace_before
                    ),
                    whitespace_after=parse_simple_whitespace(
                        config, semi.whitespace_after
                    ),
                )
        else:
            semi = MaybeSentinel.DEFAULT
        body.append(stmt_body.value.with_changes(semicolon=semi))
    return SimpleStatementPartial(
        body,
        whitespace_before=statements[0].whitespace_before,
        trailing_whitespace=trailing_whitespace,
    )
Exemplo n.º 4
0
def convert_binop(config: ParserConfig,
                  children: typing.Sequence[typing.Any]) -> typing.Any:
    leftexpr, *rightexprs = children
    if len(rightexprs) == 0:
        return leftexpr

    whitespace_before = leftexpr.whitespace_before
    leftexpr = leftexpr.value

    # Convert all of the operations that have no precedence in a loop
    for op, rightexpr in grouper(rightexprs, 2):
        if op.string not in BINOP_TOKEN_LUT:
            raise Exception(f"Unexpected token '{op.string}'!")
        leftexpr = BinaryOperation(
            left=leftexpr,
            # pyre-ignore Pyre thinks that the type of the LUT is CSTNode.
            operator=BINOP_TOKEN_LUT[op.string](
                whitespace_before=parse_parenthesizable_whitespace(
                    config, op.whitespace_before),
                whitespace_after=parse_parenthesizable_whitespace(
                    config, op.whitespace_after),
            ),
            right=rightexpr.value,
        )
    return WithLeadingWhitespace(leftexpr, whitespace_before)
Exemplo n.º 5
0
def convert_try_stmt(config: ParserConfig, children: Sequence[Any]) -> Any:
    trytoken, try_colon_token, try_suite, *rest = children
    handlers: List[ExceptHandler] = []
    orelse: Optional[Else] = None
    finalbody: Optional[Finally] = None

    for clause, colon_token, suite in grouper(rest, 3):
        if isinstance(clause, Token):
            if clause.string == "else":
                if orelse is not None:
                    raise Exception("Logic error!")
                orelse = Else(
                    leading_lines=parse_empty_lines(config, clause.whitespace_before),
                    whitespace_before_colon=parse_simple_whitespace(
                        config, colon_token.whitespace_before
                    ),
                    body=suite,
                )
            elif clause.string == "finally":
                if finalbody is not None:
                    raise Exception("Logic error!")
                finalbody = Finally(
                    leading_lines=parse_empty_lines(config, clause.whitespace_before),
                    whitespace_before_colon=parse_simple_whitespace(
                        config, colon_token.whitespace_before
                    ),
                    body=suite,
                )
            else:
                raise Exception("Logic error!")
        elif isinstance(clause, ExceptClausePartial):
            handlers.append(
                ExceptHandler(
                    body=suite,
                    type=clause.type,
                    name=clause.name,
                    leading_lines=clause.leading_lines,
                    whitespace_after_except=clause.whitespace_after_except,
                    whitespace_before_colon=parse_simple_whitespace(
                        config, colon_token.whitespace_before
                    ),
                )
            )
        else:
            raise Exception("Logic error!")

    return Try(
        leading_lines=parse_empty_lines(config, trytoken.whitespace_before),
        whitespace_before_colon=parse_simple_whitespace(
            config, try_colon_token.whitespace_before
        ),
        body=try_suite,
        handlers=tuple(handlers),
        orelse=orelse,
        finalbody=finalbody,
    )
Exemplo n.º 6
0
def convert_arglist(config: ParserConfig,
                    children: typing.Sequence[typing.Any]) -> typing.Any:
    args = []
    for argument, comma in grouper(children, 2):
        if comma is None:
            args.append(argument)
        else:
            args.append(
                argument.with_changes(comma=Comma(
                    whitespace_before=parse_parenthesizable_whitespace(
                        config, comma.whitespace_before),
                    whitespace_after=parse_parenthesizable_whitespace(
                        config, comma.whitespace_after),
                )))
    return ArglistPartial(args)
Exemplo n.º 7
0
def _gather_import_names(config: ParserConfig,
                         children: Sequence[Any]) -> ImportPartial:
    names = []
    for name, comma in grouper(children, 2):
        if comma is None:
            names.append(name)
        else:
            names.append(
                name.with_changes(comma=Comma(
                    whitespace_before=parse_parenthesizable_whitespace(
                        config, comma.whitespace_before),
                    whitespace_after=parse_parenthesizable_whitespace(
                        config, comma.whitespace_after),
                )))

    return ImportPartial(names=names)
Exemplo n.º 8
0
def convert_dotted_name(config: ParserConfig, children: Sequence[Any]) -> Any:
    left, *rest = children
    node = Name(left.string)

    for dot, right in grouper(rest, 2):
        node = Attribute(
            value=node,
            dot=Dot(
                whitespace_before=parse_parenthesizable_whitespace(
                    config, dot.whitespace_before),
                whitespace_after=parse_parenthesizable_whitespace(
                    config, dot.whitespace_after),
            ),
            attr=Name(right.string),
        )

    return node
Exemplo n.º 9
0
def convert_comparison(config: ParserConfig,
                       children: typing.Sequence[typing.Any]) -> typing.Any:
    if len(children) == 1:
        (child, ) = children
        return child

    lhs, *rest = children

    comparisons: typing.List[ComparisonTarget] = []
    for operator, comparator in grouper(rest, 2):
        comparisons.append(
            ComparisonTarget(operator=operator, comparator=comparator.value))

    return WithLeadingWhitespace(
        Comparison(left=lhs.value, comparisons=tuple(comparisons)),
        lhs.whitespace_before,
    )
Exemplo n.º 10
0
def _convert_sequencelike(
    config: ParserConfig,
    children: typing.Sequence[typing.Any],
    single_child_is_sequence: bool,
    sequence_type: typing.Union[
        typing.Type[Tuple], typing.Type[List], typing.Type[Set]
    ],
) -> typing.Any:
    if not single_child_is_sequence and len(children) == 1:
        return children[0]
    # N.B. The parent node (e.g. atom) is responsible for computing and attaching
    # whitespace information on any parenthesis, square brackets, or curly braces
    elements = []
    for wrapped_expr_or_starred_element, comma_token in grouper(children, 2):
        expr_or_starred_element = wrapped_expr_or_starred_element.value
        if comma_token is None:
            comma = MaybeSentinel.DEFAULT
        else:
            comma = Comma(
                whitespace_before=parse_parenthesizable_whitespace(
                    config, comma_token.whitespace_before
                ),
                # Only compute whitespace_after if we're not a trailing comma.
                # If we're a trailing comma, that whitespace should be consumed by the
                # TrailingWhitespace, parenthesis, etc.
                whitespace_after=(
                    parse_parenthesizable_whitespace(
                        config, comma_token.whitespace_after
                    )
                    if comma_token is not children[-1]
                    else SimpleWhitespace("")
                ),
            )

        if isinstance(expr_or_starred_element, StarredElement):
            starred_element = expr_or_starred_element
            elements.append(starred_element.with_changes(comma=comma))
        else:
            expr = expr_or_starred_element
            elements.append(Element(value=expr, comma=comma))

    # lpar/rpar are the responsibility of our parent
    return WithLeadingWhitespace(
        sequence_type(elements, lpar=(), rpar=()), children[0].whitespace_before
    )
Exemplo n.º 11
0
def _construct_nameitems(config: ParserConfig,
                         names: Sequence[Any]) -> List[NameItem]:
    nameitems: List[NameItem] = []
    for name, maybe_comma in grouper(names, 2):
        if maybe_comma is None:
            nameitems.append(NameItem(Name(name.string)))
        else:
            nameitems.append(
                NameItem(
                    Name(name.string),
                    comma=Comma(
                        whitespace_before=parse_simple_whitespace(
                            config, maybe_comma.whitespace_before),
                        whitespace_after=parse_simple_whitespace(
                            config, maybe_comma.whitespace_after),
                    ),
                ))
    return nameitems
Exemplo n.º 12
0
def convert_subscriptlist(config: ParserConfig,
                          children: typing.Sequence[typing.Any]) -> typing.Any:
    # This is a list of SubscriptElement, so construct as such by grouping every
    # subscript with an optional comma and adding to a list.
    elements = []
    for slice, comma in grouper(children, 2):
        if comma is None:
            elements.append(SubscriptElement(slice=slice.value))
        else:
            elements.append(
                SubscriptElement(
                    slice=slice.value,
                    comma=Comma(
                        whitespace_before=parse_parenthesizable_whitespace(
                            config, comma.whitespace_before),
                        whitespace_after=parse_parenthesizable_whitespace(
                            config, comma.whitespace_after),
                    ),
                ))
    return WithLeadingWhitespace(elements, children[0].whitespace_before)
Exemplo n.º 13
0
def convert_argslist(config: ParserConfig, children: Sequence[Any]) -> Any:
    params: List[Param] = []
    default_params: List[Param] = []
    star_arg: Union[Param, ParamStar, MaybeSentinel] = MaybeSentinel.DEFAULT
    kwonly_params: List[Param] = []
    star_kwarg: Optional[Param] = None

    def add_param(current_param: Optional[List[Param]],
                  param: Union[Param, ParamStar]) -> Optional[List[Param]]:
        nonlocal star_arg
        nonlocal star_kwarg

        if isinstance(param, ParamStar):
            # Only can add this if we don't already have a "*" or a "*param".
            if current_param in [params, default_params]:
                star_arg = param
                current_param = kwonly_params
            else:
                # Example code:
                #     def fn(*abc, *): ...
                # This should be unreachable, the grammar already disallows it.
                raise Exception(
                    "Cannot have multiple star ('*') markers in a single argument "
                    + "list.")
        # pyre-ignore Pyre seems to think param.star.__eq__ is not callable
        elif isinstance(param.star,
                        str) and param.star == "" and param.default is None:
            # Can only add this if we're in the params or kwonly_params section
            if current_param is params:
                params.append(param)
            elif current_param is kwonly_params:
                kwonly_params.append(param)
            else:
                # Example code:
                #     def fn(first=None, second): ...
                # This code is reachable, so we should use a PartialParserSyntaxError.
                raise PartialParserSyntaxError(
                    "Cannot have a non-default argument following a default argument."
                )
        elif (isinstance(param.star, str)
              # pyre-ignore Pyre seems to think param.star.__eq__ is not callable
              and param.star == "" and param.default is not None):
            if current_param is params:
                current_param = default_params
            # Can only add this if we're not yet at star args.
            if current_param is default_params:
                default_params.append(param)
            elif current_param is kwonly_params:
                kwonly_params.append(param)
            else:
                # Example code:
                #     def fn(**kwargs, trailing=None)
                # This should be unreachable, the grammar already disallows it.
                raise Exception(
                    "Cannot have any arguments after a kwargs expansion.")
        elif (isinstance(param.star, str)
              # pyre-ignore Pyre seems to think param.star.__eq__ is not callable
              and param.star == "*" and param.default is None):
            # Can only add this if we're in params/default_params, since
            # we only allow one of "*" or "*param".
            if current_param in [params, default_params]:
                star_arg = param
                current_param = kwonly_params
            else:
                # Example code:
                #     def fn(*first, *second): ...
                # This should be unreachable, the grammar already disallows it.
                raise Exception(
                    "Expected a keyword argument but found a starred positional "
                    + "argument expansion.")
        elif (isinstance(param.star, str)
              # pyre-ignore Pyre seems to think param.star.__eq__ is not callable
              and param.star == "**" and param.default is None):
            # Can add this in all cases where we don't have a star_kwarg
            # yet.
            if current_param is not None:
                star_kwarg = param
                current_param = None
            else:
                # Example code:
                #     def fn(**first, **second)
                # This should be unreachable, the grammar already disallows it.
                raise Exception(
                    "Multiple starred keyword argument expansions are not allowed in a "
                    + "single argument list")
        else:
            # The state machine should never end up here.
            raise Exception("Logic error!")

        return current_param

    # The parameter list we are adding to
    current: Optional[List[Param]] = params

    # We should have every other item in the group as a param or a comma by now,
    # so split them up, add commas and then put them in the appropriate group.
    for parameter, comma in grouper(children, 2):
        if comma is None:
            if isinstance(parameter, ParamStarPartial):
                # Example:
                #     def fn(abc, *): ...
                #
                # There's also the case where we have bare * with a trailing comma.
                # That's handled later.
                #
                # It's not valid to construct a ParamStar object without a comma, so we
                # need to catch the non-comma case separately.
                raise PartialParserSyntaxError(
                    "Named (keyword) arguments must follow a bare *.")
            else:
                current = add_param(current, parameter)
        else:
            comma = Comma(
                whitespace_before=parse_parenthesizable_whitespace(
                    config, comma.whitespace_before),
                whitespace_after=parse_parenthesizable_whitespace(
                    config, comma.whitespace_after),
            )
            if isinstance(parameter, ParamStarPartial):
                current = add_param(current, ParamStar(comma=comma))
            else:
                current = add_param(current,
                                    parameter.with_changes(comma=comma))

    if isinstance(star_arg, ParamStar) and len(kwonly_params) == 0:
        # Example:
        #     def fn(abc, *,): ...
        #
        # This will raise a validation error, but we want to make sure to raise a syntax
        # error instead.
        #
        # The case where there's no trailing comma is already handled by this point, so
        # this conditional is only for the case where we have a trailing comma.
        raise PartialParserSyntaxError(
            "Named (keyword) arguments must follow a bare *.")

    return Parameters(
        params=tuple(params),
        default_params=tuple(default_params),
        star_arg=star_arg,
        kwonly_params=tuple(kwonly_params),
        star_kwarg=star_kwarg,
    )