def method_invocation(node): if not isinstance(node, ast_expression.ASTMethodInvocation): return None param_types = [type_checker.get_type(arg) for arg in node.arguments] # If we have a right node, we must do field access on the left. Otherwise the # left is an ASTIdentifiers. if node.right is None: iden = node.left name, defn = iden.first_definition if defn is None: return None # When there is only one part to the identifier, it is meant to be called on # "this", and we have that defn is an ASTType of the class. if name == '': method = _resolve_further_fields(defn.definition, iden.parts, method_type=param_types, type_node=defn) else: method = _resolve_identifier(node.left, method_type=param_types) else: t_left = type_checker.get_type(node.left) method = _resolve_further_fields(t_left.definition, node.right.parts, method_type=param_types, type_node=t_left) if not isinstance(method, ast_method.ASTMethod): return None return method.return_type
def block(node): if not isinstance(node, ast_block.ASTBlock): return None # Type of all of the substatements: for c in node.children: type_checker.get_type(c) return ast_type.ASTType.ASTVoid
def assignment(node): if not isinstance(node, ast_expression.ASTAssignment): return None t_left = type_checker.get_type(node.left_expr) t_right = type_checker.get_type(node.right_expr) if not t_left.is_final and _is_assignable(t_left, t_right): return t_left return None
def array_access(node): if not isinstance(node, ast_expression.ASTArrayAccess): return None t_array = type_checker.get_type(node.array_expression) t_index = type_checker.get_type(node.index) if t_array.is_array and _is_numeric(t_index): new_t_array = copy.copy(t_array) new_t_array.is_array = False return new_t_array return None
def while_statement(node): '''Check statement: while (E) S''' if not isinstance(node, ast_while.ASTWhile): return None # While expression must be a boolean t_while_expr = type_checker.get_type(node.expression) if t_while_expr != ast_type.ASTType.ASTBoolean: return None # Make sure that the while body is typeable. type_checker.get_type(node.statement) return ast_type.ASTType.ASTVoid
def numeric_comparisons(node): '''Comparison operators for numeric types (<, <=, >, >=)''' if not isinstance(node, ast_expression.ASTBinary): return None # Check for valid operators. if node.operator not in ['<', '<=', '>', '>=']: return None t_left = type_checker.get_type(node.left_expr) t_right = type_checker.get_type(node.right_expr) if _is_numeric(t_left) and _is_numeric(t_right): return ast_type.ASTType.ASTBoolean return None
def generic_equality(node): '''Equality comparisons for assignable types (==, !=)''' if not isinstance(node, ast_expression.ASTBinary): return None # Check for valid operators. if node.operator not in ['==', '!=']: return None t_left = type_checker.get_type(node.left_expr) t_right = type_checker.get_type(node.right_expr) if _is_assignable(t_left, t_right) or _is_assignable(t_right, t_left): return ast_type.ASTType.ASTBoolean return None
def return_statement(node): '''Check statement: return E''' if not isinstance(node, ast_return.ASTReturn): return None method = type_checker.get_param('cur_method') # If there is an expression, make sure it is typeable. expr_type = None if len(node.expressions) != 0: expr_type = type_checker.get_type(node.expressions[0]) # Constructors have no return type, so you can't return something in one: if method.is_constructor and expr_type: return None # void methods should return nothing: if method.return_type == ast_type.ASTType.ASTVoid and not expr_type: return True # constructors should return nothing: if method.is_constructor and not expr_type: return True # The type you return must be assignable to the current method return type: if not _is_assignable(method.return_type, expr_type): return None return ast_type.ASTType.ASTVoid
def if_statement(node): '''Check statement: if (E) S''' if not isinstance(node, ast_if.ASTIf): return None # If expression must be a boolean t_if_expr = type_checker.get_type(node.expression) if t_if_expr != ast_type.ASTType.ASTBoolean: return None # Check that the if and else statements are typeable. type_checker.get_type(node.if_statement) if node.else_statement: type_checker.get_type(node.else_statement) return ast_type.ASTType.ASTVoid
def constructor(node): '''new D(E1, E2, .., Ek) types to D''' if not isinstance(node, ast_expression.ASTClassInstanceCreation): return None # You can't instantiate abstract classes. if node.type_node.definition.is_abstract: return None short_name = node.type_node.identifier.parts[-1] env = node.type_node.definition.environment param_types = [type_checker.get_type(x) for x in node.arguments] method_sig = (short_name, param_types) method, enclosing_type = env.lookup_method(method_sig, constructor=True) if method is not None: if method.is_protected: # JLS 6.6.2. A protected constructor can be accessed only from within the # package in which it is defined. if enclosing_type.package_name != \ type_checker.get_param('cur_class').package_name: return None return node.type_node # No matching constructor found. Whoops...! return None
def boolean_ops(node): '''Eager and lazy boolean operations (&, |, &&, ||)''' if not isinstance(node, ast_expression.ASTBinary): return None # Check for valid operators. if node.operator not in ['&', '|', '&&', '||']: return None # Make sure both operands are booleans. t_left = type_checker.get_type(node.left_expr) t_right = type_checker.get_type(node.right_expr) if t_left == ast_type.ASTType.ASTBoolean and \ t_right == ast_type.ASTType.ASTBoolean: return ast_type.ASTType.ASTBoolean return None
def numeric_math(node): '''Math operator for numeric types (+, -, *, /, %)''' if not isinstance(node, ast_expression.ASTBinary): return None # Check for valid math operators. if node.operator not in ['+', '-', '*', '/', '%']: return None # Check that both operands are numeric. t_left = type_checker.get_type(node.left_expr) t_right = type_checker.get_type(node.right_expr) if _is_numeric(t_left) and _is_numeric(t_right): # Always promote math exprs to int. return ast_type.ASTType.ASTInt return None
def instance_of(node): if not isinstance(node, ast_expression.ASTInstanceOf): return None expr_type = type_checker.get_type(node.expressions[0]) if _is_assignable(expr_type, node.type_node) or \ _is_assignable(node.type_node, expr_type): return ast_type.ASTType.ASTBoolean return None
def array_creation(node): if not isinstance(node, ast_expression.ASTArrayCreation): return None t_length_expr = type_checker.get_type(node.length_expr) t_array = node.type_node if _is_numeric(t_length_expr): new_t_array = copy.copy(t_array) new_t_array.is_array = True return new_t_array
def boolean_not(node): '''Unary boolean operator: !''' if not isinstance(node, ast_expression.ASTUnary): return None if node.operator != '!': return None t = type_checker.get_type(node.expressions[0]) if t == ast_type.ASTType.ASTBoolean: return ast_type.ASTType.ASTBoolean return None
def cast(node): if not isinstance(node, ast_cast.ASTCast): return None expr_type = type_checker.get_type(node.expressions[0]) if _is_numeric(expr_type) and _is_numeric(node.type_node): return node.type_node if _is_assignable(expr_type, node.type_node) or \ _is_assignable(node.type_node, expr_type): return node.type_node return None
def string_plus(node): '''Binary expression of string + string''' if not isinstance(node, ast_expression.ASTBinary): return None if node.operator != '+': return None # Check that both operands are of the string type. t_left = type_checker.get_type(node.left_expr) t_right = type_checker.get_type(node.right_expr) if t_left == ast_type.ASTType.ASTVoid or \ t_right == ast_type.ASTType.ASTVoid: # You can't string + on void types. return None # Check to make sure one of part typed to a string. if _is_string(t_left) or _is_string(t_right): return ast_type.ASTType.ASTString return None
def unary_math(node): '''Unary negation operator for numeric types (-)''' if not isinstance(node, ast_expression.ASTUnary): return None if node.operator != '-': return None t = type_checker.get_type(node.expressions[0]) if _is_numeric(t): # Promote to int. return ast_type.ASTType.ASTInt return None
def variable_declaration(node): if not isinstance(node, ast_variable_declaration.ASTVariableDeclaration): return None t_left = node.type_node t_right = None if node.expression is not None: t_right = type_checker.get_type(node.expression) if t_right is None or _is_assignable(t_left, t_right): return ast_type.ASTType.ASTVoid return None
def field_access(node): if not isinstance(node, ast_expression.ASTFieldAccess): return None t_left = type_checker.get_type(node.left) # Make sure that the left type can have fields. if (t_left.is_primitive and not t_left.is_array): return None type_or_decl = _resolve_further_fields(t_left.definition, node.right.parts, type_node=t_left) if isinstance(type_or_decl, ast_variable_declaration.ASTVariableDeclaration): return type_or_decl.type_node elif isinstance(type_or_decl, ast_param.ASTParam): return type_or_decl.type return type_or_decl
def for_statement(node): '''Check statement: for (S ; E ; S) S''' if not isinstance(node, ast_for.ASTFor): return None # If there is a for expression, the type of it must be a boolean. if node.expression is not None: t_for_expr = type_checker.get_type(node.expression) if t_for_expr != ast_type.ASTType.ASTBoolean: return None # If there is an init or update statement, we must make sure that it is # typeable. if node.init is not None: type_checker.get_type(node.init) if node.update is not None: type_checker.get_type(node.update) type_checker.get_type(node.statement) return ast_type.ASTType.ASTVoid