Exemplo n.º 1
0
    def parse_identifier(self) -> ast.Node:
        # Parse the identifier's name.
        identifier_token = self.consume(TokenKind.identifier)
        if identifier_token is None:
            raise self.expected_identifier()

        # Parse the optional specializers.
        backtrack = self.stream_position
        self.consume_newlines()
        if self.consume(TokenKind.lbracket):
            # We first try to parse a single annotation, with no label, so as to handle the
            # syntactic sugar consisting of omitting labels for for generic types with only a
            # single placeholder.
            self.consume_newlines()
            sugar_backtrack = self.stream_position
            try:
                specializers = {'_0': self.parse_type()}
                self.consume_newlines()
                end_token = self.consume(TokenKind.rbracket)
                if end_token is None:
                    raise self.unexpected_token(expected=']')
            except:
                self.rewind_to(sugar_backtrack)
                pairs = self.parse_sequence(TokenKind.rbracket,
                                            self.parse_specializer)
                specializers = {}
                for (name_token, value) in pairs:
                    if name_token.value in specializers:
                        raise exc.DuplicateKey(key=name_token)
                    specializers[name_token.value] = value
                end_token = self.consume(TokenKind.rbracket)
                if end_token is None:
                    raise self.unexpected_token(expected=']')
            end = end_token.source_range.end
        else:
            self.rewind_to(backtrack)
            specializers = None
            end = identifier_token.source_range.end

        return ast.Identifier(name=identifier_token.value,
                              specializers=specializers,
                              source_range=SourceRange(
                                  start=identifier_token.source_range.start,
                                  end=end))
Exemplo n.º 2
0
    def parse_prefix_expression(self) -> ast.PrefixExpression:
        # Parse the operator of the expression.
        start_token = self.consume()
        if (start_token is None) or (start_token.value
                                     not in self.prefix_operators):
            raise self.unexpected_token(expected='prefix operator')

        operator_identifier = ast.Identifier(
            name=start_token.value,
            specializers=None,
            source_range=start_token.source_range)

        # Parse the operand of the expression.
        operand = self.parse_expression()
        return ast.PrefixExpression(
            operator=operator_identifier,
            operand=operand,
            source_range=SourceRange(
                start=operator_identifier.source_range.start,
                end=operand.source_range.end))
Exemplo n.º 3
0
def p_identifier(p):
    """
    identifier : IDENTIFIER
    """
    p[0] = ast.Identifier(p[1])
Exemplo n.º 4
0
    def parse_atom(self) -> ast.Node:
        start_token = self.peek()

        if start_token.kind == TokenKind.lparen:
            atom = self.parse_parenthesized(self.parse_expression)

        elif start_token.kind in scalar_literal_kinds:
            token = self.consume()
            atom = ast.ScalarLiteral(value=token.value,
                                     source_range=token.source_range)
        elif start_token.kind == TokenKind.argref:
            token = self.consume()
            atom = ast.ArgRef(source_range=token.source_range)
        elif start_token.kind == TokenKind.identifier:
            atom = self.parse_identifier()
        elif start_token.kind == TokenKind.lbracket:
            atom = self.parse_list_literal()
        elif start_token.kind == TokenKind.lbrace:
            atom = self.parse_object_literal()
        elif start_token.kind == TokenKind.if_:
            atom = self.parse_if_expression()
        elif start_token.kind == TokenKind.match:
            atom = self.parse_match_expression()
        elif (start_token.kind
              == TokenKind.operator) and (start_token.value
                                          in self.prefix_operators):
            atom = self.parse_prefix_expression()
        else:
            raise self.unexpected_token(expected='expression')

        # Parse the optional "suffix" of the expression.
        while True:
            backtrack = self.stream_position
            self.consume_newlines()

            # If we can parse an object, we interpret it as a call expression.
            try:
                argument = self.attempt(self.parse_object_literal)
                if argument is None:
                    value = self.parse_expression()

                    # Operators that can act as both an infix and a prefix or postfix operator
                    # introduce some ambuiguity, as to how an expression like `a + b` should be
                    # parsed. The most intuitive way to interpret this expression is arguably to
                    # see `+` as an infix operator, but one may also see this as the application of
                    # `a` to the expression `+b` (i.e. `a { _0 = +b }`), or the application of `a+`
                    # the expression `b` (i.e. `a+ { _0 = b }`).
                    # We choose to desambiguise this situation by prioritizing infix expressions.
                    if isinstance(value, ast.PrefixExpression):
                        if value.operator.name in self.infix_operators:
                            self.rewind_to(backtrack)
                            break

                    # If the value is the argument reference (i.e. `$`), we use it as an object
                    # literal so that calls of the form `f $` aren't reduced to `f { _0 = $ }`.
                    if isinstance(value, ast.ArgRef):
                        argument = value
                    else:
                        key = ast.ScalarLiteral(
                            value='_0',
                            source_range=SourceRange(
                                start=self.peek().source_range.start))

                        prop = ast.ObjectLiteralProperty(
                            key=key,
                            value=value,
                            source_range=SourceRange(
                                start=key.source_range.start,
                                end=value.source_range.end))
                        argument = ast.ObjectLiteral(
                            properties=[prop], source_range=prop.source_range)

                atom = ast.CallExpression(callee=atom,
                                          argument=argument,
                                          source_range=SourceRange(
                                              start=atom.source_range.start,
                                              end=argument.source_range.end))
                continue

            except:
                self.rewind_to(backtrack)
                self.consume_newlines()

            suffix_token = self.peek()

            # An underscore corresponds to a call to a function without any argument.
            if suffix_token == TokenKind.underscore:
                end_token = self.consume()
                atom = ast.CallExpression(callee=atom,
                                          argument=None,
                                          source_range=SourceRange(
                                              start=atom.source_range.start,
                                              end=end_token.source_range.end))
                continue

            # If we can parse a postfix operator, we interpret it as a postfix expression.
            if suffix_token.kind == TokenKind.operator and (
                    suffix_token.value in self.postfix_operators):
                operator = self.consume()

                # Backtrack if the operator is also infix and the remainder of the stream can be
                # parsed as an expression.
                if operator.value in self.infix_operators:
                    if self.attempt(self.parse_expression) is not None:
                        self.rewind_to(backtrack)
                        break

                operator_identifier = ast.Identifier(
                    name=operator.value,
                    specializers=None,
                    source_range=operator.source_range)
                atom = ast.PostfixExpression(
                    operator=operator_identifier,
                    operand=atom,
                    source_range=SourceRange(
                        start=operator_identifier.source_range.start,
                        end=atom.source_range.end))
                continue

            self.rewind_to(backtrack)
            break

        return atom
Exemplo n.º 5
0
    def parse_expression(self) -> ast.Node:
        # Attempt to parse a binding.
        if self.peek().kind == TokenKind.let:
            return self.parse_binding()

        # Attempt to parse a term.
        left = self.attempt(self.parse_closure_expression) or self.parse_atom()

        # Attempt to parse the remainder of an infix expression.
        while True:
            backtrack = self.stream_position
            self.consume_newlines()
            operator = self.consume(TokenKind.operator)
            if operator is None:
                self.rewind_to(backtrack)
                break
            if operator.value not in self.infix_operators:
                raise exc.UnknownOperator(operator=operator)

            # The infix operators `.`, `?` or `!` represent attribute retrieval expressions. Just
            # like object keys, an identifier with no specializers on the right operand of an
            # attribute retrieval expression is interpreted as a character string by default.
            # Hence, we need to treat this syntactic sugar case here.
            if operator.value in {'.', '?', '!'}:
                right = self.parse_property_key()
            else:
                right = self.parse_atom()

            operator_identifier = ast.Identifier(
                name=operator.value,
                specializers=None,
                source_range=operator.source_range)

            # If the left operand is an infix expression, we should check the precedence and
            # associativity of its operator against the current one.
            if isinstance(left, ast.InfixExpression):
                lprec = self.infix_operators[left.operator.name]['precedence']
                rprec = self.infix_operators[operator.value]['precedence']
                associativity = self.infix_operators[
                    left.operator.name]['associativity']

                if ((lprec < rprec)
                        or ((left.operator.name == operator.value) and
                            (associativity == 'right'))):

                    new_right = ast.InfixExpression(
                        operator=operator_identifier,
                        left=left.right,
                        right=right,
                        source_range=SourceRange(
                            start=left.right.source_range.start,
                            end=right.source_range.end))
                    left = ast.InfixExpression(
                        operator=left.operator,
                        left=left.left,
                        right=new_right,
                        source_range=SourceRange(
                            start=left.left.source_range.start,
                            end=right.source_range.end))
                    continue

            left = ast.InfixExpression(operator=operator_identifier,
                                       left=left,
                                       right=right,
                                       source_range=SourceRange(
                                           start=left.source_range.start,
                                           end=right.source_range.end))
            continue

        return left
Exemplo n.º 6
0
def p_identifier(p):
    '''
    identifier : IDENTIFIER
    '''
    p[0] = ast.Identifier(p[1])