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 __getitem__(self, dot_path): """ Return the type of the given item. :type dot_path: ast.DotPath :param dot_path: The path to follow :return: """ if self.ctor_properties is None: raise exception.BananaTypeCheckerBug( "Component type can't have properties") if len(dot_path.properties) == 0: for arg in self.ctor_properties: if arg.param_name == dot_path.varname.inner_val(): return arg.param_type else: for arg in self.ctor_properties: if arg.param_name == dot_path.varname.inner_val(): if isinstance(arg.param_type, Object): return arg.param_type[dot_path.next_dot_path()] else: raise exception.BananaPropertyDoesNotExists( dot_path.next_dot_path(), arg.param_type) raise exception.BananaPropertyDoesNotExists(dot_path, on_type=self)
def __getitem__(self, key): # a.b or a."b" if isinstance(key, ast.Ident) or isinstance(key, ast.StringLit): if key.inner_val() not in self.props: raise exception.BananaPropertyDoesNotExists(key, on_type=self) return self.props[key.inner_val()] # a.b.c if isinstance(key, ast.DotPath): if key.varname.inner_val() not in self.props: raise exception.BananaPropertyDoesNotExists(key.varname, on_type=self) sub_object = self.props[key.varname.inner_val()] if len(key.properties) == 0: return sub_object # Recurse if isinstance(sub_object, Object): return sub_object[key.next_dot_path()] if isinstance(sub_object, Any): return sub_object raise exception.BananaPropertyDoesNotExists(key.next_dot_path(), on_type=sub_object) raise exception.BananaTypeCheckerBug( "Unreachable code in Object.__getitem__ reached.")
def get_type(self, var, statement_index=None): variables = self.get_variables(statement_index) if isinstance(var, ast.Ident): if var in variables: return variables[var] else: raise exception.BananaUnknown(var) # If we encounter a dot path: if isinstance(var, ast.DotPath): if var.varname in variables: if len(var.properties) > 0: return variables[var.varname][var.next_dot_path()] else: return variables[var.varname] else: raise exception.BananaUnknown(var.varname) raise exception.BananaTypeCheckerBug("Unkown type for {}".format(var))
def __setitem__(self, dot_path, value): """ Attempt to set the value at 'dot_path' to 'value'. :type dot_path: ast.DotPath :param dot_path: The path of the property :type value: String | Enum | Object | Number :param value: The new type to set. """ if self.ctor_properties is None: raise exception.BananaTypeCheckerBug( "Component type can't have properties") if len(dot_path.properties) == 0: for arg in self.ctor_properties: if arg.param_name == dot_path.varname.inner_val(): if not can_be_cast_to(value, arg.param_type): raise exception.BananaArgumentTypeError( expected_type=arg.param_type, received_type=value, where=dot_path.span) else: return else: for arg in self.ctor_properties: if arg.param_name == dot_path.varname.inner_val(): if isinstance(arg.param_type, Any): return elif isinstance(arg.param_type, Object): next_dot_path = dot_path.next_dot_path() sub_arg_type = arg.param_type[next_dot_path] if not can_be_cast_to(value, sub_arg_type): raise exception.BananaArgumentTypeError( expected_type=sub_arg_type, received_type=value, where=next_dot_path.span) else: return else: raise exception.BananaPropertyDoesNotExists( dot_path.next_dot_path(), arg.param_type) raise exception.BananaPropertyDoesNotExists(dot_path, on_type=self)
def typeck_component(component, type_table): """ Type-check the provided component. Returns the appropriate subclass of util.Component if successful, or raise an exception if there's an error. :type component: ast.Component :param component: The component ast node. :type type_table: typetbl.TypeTable :param type_table: the type table. :rtype: u.Source | u.Sink | u.Voter | u.Ldp | u.Sml | u.Ingestor :return: Returns the appropriate type for the component. """ # TODO(Joan): This wont't work for type that are defined # TODO(Joan): at the language level. We need a registration service # TODO(Joan): to manage the Types of component that we can create # TODO(Joan): instead of this hacky function call. try: component_type = introspect.get_class_by_name(component.type_name.val) comp_params = component_type.get_params() except exception_monanas.MonanasNoSuchClassError: raise exception.BananaUnknown(component) # Compute the type of the component if issubclass(component_type, source.BaseSource): comp_type = u.Source(component_type.__name__, comp_params) elif issubclass(component_type, sink.BaseSink): comp_type = u.Sink(component_type.__name__, comp_params) elif issubclass(component_type, sml.BaseSML): comp_type = u.Sml(component_type.__name__, comp_params) elif issubclass(component_type, voter.BaseVoter): comp_type = u.Voter(component_type.__name__, comp_params) elif issubclass(component_type, ldp.BaseLDP): comp_type = u.Ldp(component_type.__name__, comp_params) elif issubclass(component_type, ingestor.BaseIngestor): comp_type = u.Ingestor(component_type.__name__, comp_params) else: raise exception.BananaTypeCheckerBug( "Couldn't find a type for '{}'".format(component.type_name.val)) # Type check the parameters if len(component.args) > len(comp_params): raise exception.BananaComponentTooManyParams(component.span) # Does saying that parameter should either all have a name # or non at all satisfying? -> Yes # Are parameter all named? all_named = -1 for arg in component.args: if arg.arg_name is not None: if all_named == 0: raise exception.BananaComponentMixingParams(arg.span, False) all_named = 1 else: if all_named == 1: raise exception.BananaComponentMixingParams(arg.span, True) all_named = 0 if all_named == 1: for arg in component.args: param = filter(lambda x: x.param_name == arg.arg_name.inner_val(), comp_params) if len(param) != 1: raise exception.BananaComponentIncorrectParamName( component=component.type_name, found=arg.arg_name) param = param[0] expr_type = typeck_rhs(arg.value, type_table) if not u.can_be_cast_to(expr_type, param.param_type): raise exception.BananaArgumentTypeError( where=arg, expected_type=param.param_type, received_type=expr_type) else: for arg, param in zip(component.args, comp_params): arg.arg_name = ast.Ident(arg.span, param.param_name) expr_type = typeck_rhs(arg.value, type_table) if not u.can_be_cast_to(expr_type, param.param_type): raise exception.BananaArgumentTypeError( where=arg, expected_type=param.param_type, received_type=expr_type) return comp_type