def check_type(old_type, new_type): if new_type == old_type: return old_type elif new_type == u.String(): if must_be_number: raise exception.BananaTypeError(expected_type=u.Number, found_type=new_type) if old_type is None: return new_type elif u.can_to_str(old_type): return new_type else: raise exception.BananaTypeError(expected_type=old_type, found_type=new_type) elif new_type == u.Number(): if old_type is None: return new_type elif old_type == u.String(): return old_type elif not old_type == u.Number(): raise exception.BananaTypeError(expected_type=old_type, found_type=new_type) else: raise exception.BananaTypeError(expected_type=old_type, found_type=new_type)
def typeck_connections(connection, type_table): """ Once all variable have been type-checked, we can try to type-check connections. :type connection: monasca_analytics.banana.grammar.ast.Connection :param connection: The connection to type-check :type type_table: monasca_analytics.banana.typeck.type_table.TypeTable :param type_table: The table with all variable already type-checked. :raise Raise an exception if there's a type error in connections. """ if connection is not None: for ident_from, ident_to in connection.connections: type_from = type_table.get_type(ident_from) type_to = type_table.get_type(ident_to) if not util.is_comp(type_from): raise exception.BananaTypeError(expected_type=util.Component(), found_type=type_from) if not util.is_comp(type_to): raise exception.BananaTypeError(expected_type=util.Component(), found_type=type_to) if type(type_to) not in valid_connections_types[type(type_from)]: possible_types = map(lambda x: x.__name__, valid_connections_types[type(type_from)]) raise exception.BananaConnectionError(connection.span, ident_from, ident_to, type_from, possible_types)
def attach_to_root(root_obj, obj1, span, erase_existing=False): """ Attach the object obj1 to the root_obj object type. :type root_obj: Object :param root_obj: The root object :type obj1: Object :param obj1: The object to attach. :type span: Span :param span: The span for this change. :type erase_existing: bool :param erase_existing: Set to true if the root type should always be erased. """ for key, child_type in obj1.props.iteritems(): if key in root_obj.props: root_sub_type = root_obj.props[key] # Both are object -> recurse if isinstance(root_sub_type, Object) and\ isinstance(child_type, Object): attach_to_root(root_sub_type, child_type, span, erase_existing) elif erase_existing: root_obj.props[key] = child_type else: raise exception.BananaTypeError(expected_type=root_sub_type, found_type=child_type, span=span) else: # We can simply attach the new type! root_obj.props[key] = child_type
def set_type(self, var, _type, statement_index): """ Set the type for the given var to _type. :type var: ast.Ident | ast.DotPath :param var: The var to set a type. :type _type: util.Object | util.Component | util.String | util.Number :param _type: The type for the var. :type statement_index: int :param statement_index: The statement at which this assignment was made. """ if _type is None: raise exception.BananaTypeCheckerBug( "'None' is not a valid banana type" ) if isinstance(var, ast.Ident): self._check_needs_for_snapshot(var, _type, statement_index) self._variables[var] = _type return if isinstance(var, ast.DotPath): if util.is_comp(_type) and len(var.properties) > 0: raise exception.BananaAssignCompError(var.span) if len(var.properties) == 0: self._check_needs_for_snapshot( var.varname, _type, statement_index ) self._variables[var.varname] = _type else: if var.varname in self._variables: var_type = self._variables[var.varname] if isinstance(var_type, util.Object): new_type = util.create_object_tree( var.next_dot_path(), _type) util.attach_to_root(var_type, new_type, var.span, erase_existing=True) elif isinstance(var_type, util.Component): var_type[var.next_dot_path()] = _type else: raise exception.BananaTypeError( expected_type=util.Object, found_type=type(var) ) # Var undeclared, declare its own type else: new_type = util.create_object_tree(var.next_dot_path(), _type) self._variables[var.varname] = new_type return raise exception.BananaTypeCheckerBug("Unreachable code reached.")
def typeck_expr(expr, type_table): """ Type-check the given expression. If the typecheck pass, the resulting type will be used for the strategy to use when evaluating this expression. :type expr: ast.Expr :param expr: The expression to typecheck. :type type_table: typetbl.TypeTable :param type_table: Type of the table :rtype: u.Number | u.String :return: Returns the type of the expression if possible :raise: Raise an exception """ # In the case where we are just wrapping around # only one expression, the logic below # needs to be skipped. if len(expr.expr_tree) == 1: return typeck_rhs(expr.expr_tree[0], type_table) _type = None must_be_number = False def check_type(old_type, new_type): if new_type == old_type: return old_type elif new_type == u.String(): if must_be_number: raise exception.BananaTypeError(expected_type=u.Number, found_type=new_type) if old_type is None: return new_type elif u.can_to_str(old_type): return new_type else: raise exception.BananaTypeError(expected_type=old_type, found_type=new_type) elif new_type == u.Number(): if old_type is None: return new_type elif old_type == u.String(): return old_type elif not old_type == u.Number(): raise exception.BananaTypeError(expected_type=old_type, found_type=new_type) else: raise exception.BananaTypeError(expected_type=old_type, found_type=new_type) def allowed_symbol(current_type): if current_type == u.String(): return ['+'] else: return ['+', '-', '*', '/'] for el in expr.expr_tree: if isinstance(el, ast.StringLit): _type = check_type(_type, u.String()) elif isinstance(el, ast.Number): _type = check_type(_type, u.Number()) elif isinstance(el, ast.Ident): ident_type = type_table.get_type(el) _type = check_type(_type, ident_type) elif isinstance(el, ast.DotPath): dotpath_type = type_table.get_type(el) _type = check_type(_type, dotpath_type) elif isinstance(el, ast.Expr): _type = check_type(_type, typeck_expr(el, type_table)) elif isinstance(el, basestring): if el not in allowed_symbol(_type): raise exception.BananaUnknownOperator(expr.span, el, _type) if el in ['-', '*', '/']: must_be_number = True else: raise exception.BananaTypeError(expected_type=[ u.Number.__name__, u.String.__name__, u.Object.__name__ ], ) # The final type if we made until here! return _type