Exemplo n.º 1
0
    def visit_BinOp(self, node):
        """

        Args:
            node:

        Returns:

        """
        # Handle left and right
        left = self.visit(node.left)
        right = self.visit(node.right)

        # Handle operation
        if isinstance(left, UnknownType) or isinstance(right, UnknownType):
            return UnknownType()
        elif type(node.op) in VALID_BINOP_TYPES:
            op_lookup = VALID_BINOP_TYPES[type(node.op)]
            if type(left) in op_lookup:
                op_lookup = op_lookup[type(left)]
                if type(right) in op_lookup:
                    op_lookup = op_lookup[type(right)]
                    return op_lookup(left, right)
        self._issue(
            incompatible_types(self.locate(),
                               node.op,
                               left,
                               right,
                               report=self.report))
        return UnknownType()
Exemplo n.º 2
0
    def visit_Name(self, node):
        """

        Args:
            node:

        Returns:

        """
        name = node.id
        if name == "___":
            self._issue(unconnected_blocks(self.locate()))
        if isinstance(node.ctx, ast.Load):
            if name == "True" or name == "False":
                return BoolType()
            elif name == "None":
                return NoneType()
            else:
                variable = self.find_variable_scope(name)
                builtin = get_builtin_function(name)
                if not variable.exists and builtin:
                    return builtin
                else:
                    state = self.load_variable(name)
                    return state.type
        else:
            variable = self.find_variable_scope(name)
            if variable.exists:
                return variable.state.type
            else:
                return UnknownType()
Exemplo n.º 3
0
        def definition(tifa, call_type, call_name, parameters, call_position):
            """

            Args:
                tifa:
                call_type:
                call_name:
                parameters:
                call_position:

            Returns:

            """
            function_scope = NewScope(self, definitions_scope)
            with function_scope:
                # Process arguments
                args = node.args.args
                if len(args) != len(parameters):
                    self._issue(
                        incorrect_arity(position, "lambda",
                                        report=self.report))
                # TODO: Handle special types of parameters
                for arg, parameter in zip(args, parameters):
                    name = arg.arg
                    if parameter is not None:
                        parameter = parameter.clone_mutably()
                        self.store_variable(name, parameter, position)
                if len(args) < len(parameters):
                    for undefined_parameter in parameters[len(args):]:
                        self.store_variable(name, UnknownType(), position)
                return_value = self.visit(node.body)
            return return_value
Exemplo n.º 4
0
    def visit(self, node):
        """
        Process this node by calling its appropriate visit_*

        Args:
            node (AST): The node to visit
        Returns:
            Type: The type calculated during the visit.
        """
        # Start processing the node
        self.node_chain.append(node)
        self.ast_id += 1

        # Actions after return?
        if len(self.scope_chain) > 1:
            return_state = self.find_variable_scope("*return")
            if return_state.exists and return_state.in_scope:
                if return_state.state.set == "yes":
                    self._issue(
                        action_after_return(self.locate(), report=self.report))

        # No? All good, let's enter the node
        self.final_node = node
        result = ast.NodeVisitor.visit(self, node)
        # If a node failed to return something, return the UNKNOWN TYPE
        if result is None:
            result = UnknownType()
        self.analysis.node_types[node] = result

        # Pop the node out of the chain
        self.ast_id -= 1
        self.node_chain.pop()

        return result
Exemplo n.º 5
0
    def visit_AugAssign(self, node):
        """

        Args:
            node:

        Returns:

        """
        # Handle value
        right = self.visit(node.value)
        # Handle target
        left = self.visit(node.target)
        # Target is always a Name, Subscript, or Attribute
        name = self.identify_caller(node.target)

        # Handle operation
        self.load_variable(name)
        if isinstance(left, UnknownType) or isinstance(right, UnknownType):
            return UnknownType()
        elif type(node.op) in VALID_BINOP_TYPES:
            op_lookup = VALID_BINOP_TYPES[type(node.op)]
            if type(left) in op_lookup:
                op_lookup = op_lookup[type(left)]
                if type(right) in op_lookup:
                    op_lookup = op_lookup[type(right)]
                    result_type = op_lookup(left, right)
                    self.assign_target(node.target, result_type)
                    return result_type
        self._issue(
            incompatible_types(self.locate(),
                               node.op,
                               left,
                               right,
                               report=self.report))
Exemplo n.º 6
0
    def visit_Call(self, node):
        """

        Args:
            node:

        Returns:

        """
        # Handle func part (Name or Attribute)
        function_type = self.visit(node.func)
        # TODO: Need to grab the actual type in some situations
        callee = self.identify_caller(node)

        # Handle args
        arguments = [self.visit(arg) for arg in node.args] if node.args else []

        # Check special common mistakes

        # TODO: Handle keywords
        # TODO: Handle star args
        # TODO: Handle kwargs
        if isinstance(function_type, FunctionType):
            # Test if we have called this definition before
            if function_type.definition not in self.definition_chain:
                self.definition_chain.append(function_type.definition)
                # Function invocation
                result = function_type.definition(self, function_type, callee,
                                                  arguments, self.locate())
                self.definition_chain.pop()
                return result
            else:
                self._issue(
                    recursive_call(self.locate(), callee, report=self.report))
        elif isinstance(function_type, ClassType):
            constructor = function_type.get_constructor().definition
            self.definition_chain.append(constructor)
            result = constructor(self, constructor, callee, arguments,
                                 self.locate())
            self.definition_chain.pop()
            if '__init__' in function_type.fields:
                initializer = function_type.fields['__init__']
                if isinstance(initializer, FunctionType):
                    self.definition_chain.append(initializer)
                    initializer.definition(self, initializer, result,
                                           [result] + arguments, self.locate())
                    self.definition_chain.pop()
            return result
        elif isinstance(function_type, (NumType, StrType, BoolType, NoneType)):
            self._issue(
                not_a_function(self.locate(),
                               callee,
                               function_type,
                               report=self.report))
        return UnknownType()
Exemplo n.º 7
0
def get_builtin_module(name):
    """
    Given the name of the module, retrieve its TIFA representation.

    Args:
        name:

    Returns:

    """
    return BUILTIN_MODULES.get(name, UnknownType())
Exemplo n.º 8
0
    def visit_UnaryOp(self, node):
        """

        Args:
            node:

        Returns:

        """
        # Handle operand
        operand = self.visit(node.operand)

        if isinstance(node.op, ast.Not):
            return BoolType()
        elif isinstance(operand, UnknownType):
            return UnknownType()
        elif type(node.op) in VALID_UNARYOP_TYPES:
            op_lookup = VALID_UNARYOP_TYPES[type(node.op)]
            if type(operand) in op_lookup:
                return op_lookup[type(operand)]()
        return UnknownType()
Exemplo n.º 9
0
    def load_variable(self, name, position=None):
        """
        Retrieve the variable with the given name.

        Args:
            position:
            name (str): The unqualified name of the variable. If the variable is
                        not found in the current scope or an enclosing scope, all
                        other scopes will be searched to see if it was read out
                        of scope.
        Returns:
            State: The current state of the variable.
        """
        full_name = self._scope_chain_str(name)
        current_path = self.path_chain[0]
        variable = self.find_variable_scope(name)
        if position is None:
            position = self.locate()
        if not variable.exists:
            out_of_scope_var = self.find_variable_out_of_scope(name)
            # Create a new instance of the variable on the current path
            if out_of_scope_var.exists:
                self._issue(read_out_of_scope(self.locate(), name))
            else:
                self._issue(initialization_problem(self.locate(), name))
            new_state = State(name, [],
                              UnknownType(),
                              'load',
                              position,
                              read='yes',
                              set='no',
                              over='no')
            self.name_map[current_path][full_name] = new_state
        else:
            new_state = self.trace_state(variable.state, "load", position)
            if variable.state.set == 'no':
                self._issue(initialization_problem(self.locate(), name))
            if variable.state.set == 'maybe':
                if name != '*return':
                    self._issue(
                        possible_initialization_problem(self.locate(), name))
            new_state.read = 'yes'
            if not variable.in_scope:
                self.name_map[current_path][variable.scoped_name] = new_state
            else:
                self.name_map[current_path][full_name] = new_state
        return new_state
Exemplo n.º 10
0
def get_pedal_type_from_value(value, type_space=None) -> Type:
    """ Converts the Python value to a Pedal Type """
    if isinstance(value, bool):
        return BoolType()
    if isinstance(value, (int, float, complex)):
        return NumType()
    if isinstance(value, str):
        return StrType()
    if isinstance(value, type(None)):
        return NoneType()
    if isinstance(value, tuple):
        return TupleType(
            (get_pedal_type_from_value(t, type_space) for t in value))
    if isinstance(value, set):
        return SetType(
            (get_pedal_type_from_value(t, type_space) for t in value))
    if isinstance(value, frozenset):
        return FrozenSetType(
            (get_pedal_type_from_value(t, type_space) for t in value))
    if isinstance(value, list):
        if value:
            return ListType(empty=False,
                            subtype=get_pedal_type_from_value(value[0]))
        else:
            return ListType(empty=True)
    if isinstance(value, dict):
        if not value:
            return DictType(empty=True)
        if all(isinstance(value, str) for k in value.keys()):
            return DictType(literals=[LiteralStr(s) for s in value.keys()],
                            values=[
                                get_pedal_type_from_value(vv, type_space)
                                for vv in value.values()
                            ])
        return DictType(keys=[
            get_pedal_type_from_ast(k, type_space) for k in value.keys()
        ],
                        values=[
                            get_pedal_type_from_ast(vv, type_space)
                            for vv in value.values()
                        ])
    return UnknownType()
Exemplo n.º 11
0
    def visit_IfExp(self, node):
        """

        Args:
            node:

        Returns:

        """
        # Visit the conditional
        self.visit(node.test)

        # Visit the body
        body = self.visit(node.body)

        # Visit the orelse
        orelse = self.visit(node.orelse)

        if are_types_equal(body, orelse):
            return body

        # TODO: Union type?
        return UnknownType()
Exemplo n.º 12
0
        def definition(tifa, call_type, call_name, parameters, call_position):
            """

            Args:
                tifa:
                call_type:
                call_name:
                parameters:
                call_position:

            Returns:

            """
            function_scope = NewScope(self, definitions_scope)
            with function_scope:
                # Process arguments
                args = node.args.args
                if len(args) != len(parameters):
                    self._issue(
                        incorrect_arity(self.locate(),
                                        function_name,
                                        report=self.report))
                # TODO: Handle special types of parameters
                for arg, parameter in zip(args, parameters):
                    name = arg.arg
                    if arg.annotation:
                        self.visit(arg.annotation)
                        annotation = get_pedal_type_from_annotation(
                            arg.annotation, self)
                        # TODO: Use parameter information to "fill in" empty lists
                        if isinstance(parameter, ListType) and isinstance(
                                annotation, ListType):
                            if isinstance(parameter.subtype, UnknownType):
                                parameter.subtype = annotation.subtype
                        # TODO: Check that arg.type and parameter type match!
                        if not are_types_equal(annotation, parameter, True):
                            self._issue(
                                parameter_type_mismatch(
                                    self.locate(), name, annotation,
                                    parameter))
                    if parameter is not None:
                        parameter = parameter.clone_mutably()
                        self.create_variable(name, parameter, position)
                # Too many arguments
                if len(args) < len(parameters):
                    for undefined_parameter in parameters[len(args):]:
                        self.create_variable(name, UnknownType(), position)
                # Not enough arguments
                if len(args) > len(parameters):
                    for arg in args[len(parameters):]:
                        if arg.annotation:
                            self.visit(arg.annotation)
                            annotation = get_pedal_type_from_annotation(
                                arg.annotation, self)
                        else:
                            annotation = UnknownType()
                        self.create_variable(arg.arg, annotation, position)
                self.visit_statements(node.body)
                return_state = self.find_variable_scope("*return")
                return_value = NoneType()
                # TODO: Figure out if we are not returning something when we should
                # If the pseudo variable exists, we load it and get its type
                if return_state.exists and return_state.in_scope:
                    return_state = self.load_variable("*return", call_position)
                    return_value = return_state.type
                    if node.returns:
                        # self.visit(node.returns)
                        returns = get_pedal_type_from_annotation(
                            node.returns, self)
                        if not are_types_equal(return_value, returns, True):
                            self._issue(
                                multiple_return_types(
                                    return_state.position,
                                    returns.precise_description(),
                                    return_value.precise_description(),
                                    report=self.report))
            return return_value
Exemplo n.º 13
0
def get_builtin_function(name):
    """

    Args:
        name:

    Returns:

    """
    # Void Functions
    if name == "print":
        return FunctionType(name="print", returns=NoneType())
    # Math Functions
    elif name in ("int", "abs", "float", "len", "ord", "pow", "round", "sum"):
        return FunctionType(name=name, returns=NumType())
    # Boolean Functions
    elif name in ("bool", "all", "any", "isinstance"):
        return FunctionType(name=name, returns=BoolType())
    # String Functions
    elif name in ("str", 'chr', 'bin', 'repr', 'input'):
        return FunctionType(name=name, returns=StrType())
    # File Functions
    elif name == "open":
        return FunctionType(name="open", returns=FileType())
    # List Functions
    elif name == "map":
        return FunctionType(name="map", returns=ListType(empty=False))
    elif name == "list":
        return FunctionType(name="list",
                            definition=_builtin_sequence_constructor(ListType))
    # Set Functions
    elif name == "set":
        return FunctionType(name="set",
                            definition=_builtin_sequence_constructor(SetType))
    # Dict Functions
    elif name == "dict":
        return FunctionType(name="dict", returns=DictType())
    # Pass through
    elif name == "sorted":
        return FunctionType(name="sorted", returns='identity')
    elif name == "reversed":
        return FunctionType(name="reversed", returns='identity')
    elif name == "filter":
        return FunctionType(name="filter", returns='identity')
    # Special Functions
    elif name == "type":
        return FunctionType(name="type", returns=UnknownType())
    elif name == "range":
        return FunctionType(name="range",
                            returns=ListType(NumType(), empty=False))
    elif name == "dir":
        return FunctionType(name="dir",
                            returns=ListType(StrType(), empty=False))
    elif name == "max":
        return FunctionType(name="max", returns='element')
    elif name == "min":
        return FunctionType(name="min", returns='element')
    elif name == "zip":
        return FunctionType(name="zip", returns=_builtin_zip)
    elif name == "__import__":
        return FunctionType(name="__import__", returns=ModuleType())
    elif name == "globals":
        return FunctionType(name="globals",
                            returns=DictType(keys=StrType(),
                                             values=UnknownType(),
                                             empty=False))
    elif name in ("classmethod", "staticmethod"):
        return FunctionType(name=name, returns='identity')
    elif name in ("__name__", ):
        return StrType()
Exemplo n.º 14
0
"""

from pedal.types.definitions import (UnknownType, FunctionType, NumType,
                                     NoneType, BoolType, TupleType, ListType,
                                     StrType, FileType, DictType, ModuleType,
                                     SetType, DayType, TimeType, LiteralNum)

BUILTIN_MODULES = {
    'pprint':
    ModuleType(
        'pprint',
        fields={'pprint': FunctionType(name='pprint', returns=NoneType())}),
    'json':
    ModuleType('json',
               fields={
                   'loads': FunctionType(name='loads', returns=UnknownType()),
                   'dumps': FunctionType(name='dumps', returns=StrType())
               }),
    'random':
    ModuleType(
        'random',
        fields={'randint': FunctionType(name='randint', returns=NumType())}),
    'string':
    ModuleType('string',
               fields={
                   'letters': StrType(empty=False),
                   'digits': StrType(empty=False),
                   'ascii_letters': StrType(empty=False),
                   'punctuation': StrType(empty=False),
                   'printable': StrType(empty=False),
                   'whitespace': StrType(empty=False),
Exemplo n.º 15
0
def get_pedal_type_from_ast(value: ast.AST, type_space=None) -> Type:
    """
    Determines the Pedal Type from this ast node.
    Args:
        value (ast.AST): An AST node.

    Returns:
        Type: A Pedal Type
    """
    try:
        if isinstance(value, ast.Constant):
            return get_pedal_type_from_value(value.value, type_space)
    except AttributeError as e:
        pass
    if isinstance(value, ast.Name):
        return get_pedal_type_from_str(value.id, type_space)
    elif isinstance(value, ast.Str):
        return StrType(bool(value.s))
    elif isinstance(value, ast.List):
        return ListType(subtype=(get_pedal_type_from_ast(
            value.elts[0], type_space) if value.elts else None),
                        empty=not bool(value.elts))
    elif isinstance(value, ast.Set):
        return SetType(subtype=(get_pedal_type_from_ast(
            value.elts[0], type_space) if value.elts else None),
                       empty=not bool(value.elts))
    elif isinstance(value, ast.Tuple):
        return TupleType(subtypes=[
            get_pedal_type_from_ast(e, type_space) for e in value.elts
        ])
    elif isinstance(value, ast.Dict):
        if not value.keys:
            return DictType(empty=True)
        if all(isinstance(k, ast.Str) for k in value.keys):
            return DictType(literals=[LiteralStr(s.s) for s in value.keys],
                            values=[
                                get_pedal_type_from_ast(vv, type_space)
                                for vv in value.values
                            ])
        return DictType(
            keys=[get_pedal_type_from_ast(k, type_space) for k in value.keys],
            values=[
                get_pedal_type_from_ast(vv, type_space) for vv in value.values
            ])
    # Support new style subscripts (e.g., ``list[int]``)
    elif ((IS_PYTHON_39 and isinstance(value, ast.Subscript))
          or isinstance(value, ast.Subscript)
          and isinstance(value.slice, ast.Index)):
        if IS_PYTHON_39:
            slice = value.slice
        else:
            slice = value.slice.value
        if isinstance(value.value, ast.Name):
            if isinstance(slice, ast.Name):
                subtype = get_pedal_type_from_str(slice.id)
                if value.value.id == "list":
                    return ListType(subtype=subtype, empty=False)
                if value.value.id == "set":
                    return SetType(subtype=subtype, empty=False)
                if value.value.id == "tuple":
                    return TupleType(subtypes=(subtype, ))
                if value.value.id == "frozenset":
                    return FrozenSetType(subtype=subtype, empty=False)
            elif isinstance(slice, ast.Tuple):
                subtypes = [
                    get_pedal_type_from_ast(e, type_space) for e in slice.elts
                ]
                if value.value.id == "tuple":
                    return TupleType(subtypes=subtypes)
                elif value.value.id == "dict" and len(subtypes) == 2:
                    return DictType(keys=subtypes[0], values=subtypes[1])
    # Top-level Module, parse it and get it back
    if isinstance(value, ast.Module) and value.body:
        if isinstance(value.body[0], ast.Expr):
            return get_pedal_type_from_ast(value.body[0].value, type_space)
    return UnknownType()