def construct(self): """ Construct a resolved expression for this. :rtype: EqExpr """ cexpr = construct(self.expr) return Eq.make_expr( cexpr, LiteralExpr(cexpr.type.nullexpr(), cexpr.type) )
def construct_static(cls, cexpr, abstract_expr=None): result = (cls.construct_node(cexpr) if cexpr.type.is_ast_node or cexpr.type.is_entity_type else Eq.make_expr( cexpr, NullExpr(cexpr.type))) result.abstract_expr = abstract_expr return result
def construct(self): """ Construct a resolved expression for this. :rtype: ResolvedExpression """ # Add the variables created for this expression to the current scope scope = PropertyDef.get_scope() for _, var, _ in self.matchers: scope.add(var.local_var) matched_expr = construct(self.matched_expr) check_source_language(issubclass(matched_expr.type, ASTNode) or matched_expr.type.is_env_element_type, 'Match expressions can only work on AST nodes ' 'or env elements') # Create a local variable so that in the generated code, we don't have # to re-compute the prefix for each type check. matched_abstract_var = AbstractVariable( names.Name('Match_Prefix'), type=matched_expr.type, create_local=True ) PropertyDef.get_scope().add(matched_abstract_var.local_var) matched_var = construct(matched_abstract_var) constructed_matchers = [] # Check (i.e. raise an error if no true) the set of matchers is valid: # * all matchers must target allowed types, i.e. input type subclasses; for typ, var, expr in self.matchers: if typ is not None: check_source_language( typ.matches(matched_expr.type), 'Cannot match {} (input type is {})'.format( typ.name().camel, matched_expr.type.name().camel ) ) else: # The default matcher (if any) matches the most general type, # which is the input type. var.set_type(matched_expr.type) constructed_matchers.append((construct(var), construct(expr))) # * all possible input types must have at least one matcher. Also warn # if some matchers are unreachable. self._check_match_coverage(matched_expr.type) # Compute the return type as the unification of all branches _, expr = constructed_matchers[-1] rtype = expr.type for _, expr in constructed_matchers: check_source_language( expr.type.matches(rtype), "Wrong type for match result" " expression: got {} but expected {} or sub/supertype".format( expr.type.name().camel, rtype.name().camel ) ) rtype = expr.type.unify(rtype) # This is the expression execution will reach if we have a bug in our # code (i.e. if matchers did not cover all cases). result = UnreachableExpr(rtype) # Wrap this "failing" expression with all the cases to match in the # appropriate order, so that in the end the first matchers are tested # first. for match_var, expr in reversed(constructed_matchers): casted = Cast.Expr(matched_var, match_var.type, result_var=match_var) guard = Not.make_expr( Eq.make_expr( casted, LiteralExpr(casted.type.nullexpr(), casted.type) ) ) if expr.type != rtype: # We already checked that type matches, so only way this is # true is if expr.type is an ASTNode type derived from # rtype. In that case, we need an explicity upcast. expr = Cast.Expr(expr, rtype) result = If.Expr(guard, expr, result, rtype) return Let.Expr( [matched_var], [matched_expr], BindingScope(result, [construct(var) for _, var, _ in self.matchers]) )
def construct_static(cexpr): return Eq.make_expr( cexpr, LiteralExpr(cexpr.type.nullexpr(), cexpr.type) )
def construct(self): """ Construct a resolved expression for this. :rtype: ResolvedExpression """ # Add the variables created for this expression to the current scope scope = PropertyDef.get_scope() for _, v, _ in self.matchers: scope.add(v.local_var) matched_expr = construct(self.matched_expr) check_source_language(issubclass(matched_expr.type, ASTNode), 'Match expressions can only work on AST nodes') # Yes, the assertion below is what we just checked above, but unlike # check_source_language, assert_type provides type information to # PyCharm's static analyzer. matched_type = assert_type(matched_expr.type, ASTNode) constructed_matchers = [] # Check (i.e. raise an error if no true) the set of matchers is valid: # * all matchers must target allowed types, i.e. input type subclasses; for t, v, e in self.matchers: if t is not None: check_source_language( t.matches(matched_expr.type), 'Cannot match {} (input type is {})'.format( t.name().camel, matched_expr.type.name().camel ) ) else: # The default matcher (if any) matches the most general type, # which is the input type. v.set_type(matched_expr.type) constructed_matchers.append((construct(v), construct(e))) # * all possible input types must have at least one matcher. Also warn # if some matchers are unreachable. self._check_match_coverage(matched_type) # Compute the return type as the unification of all branches _, expr = constructed_matchers[-1] rtype = expr.type for _, expr in constructed_matchers: check_source_language( expr.type.matches(rtype), "Wrong type for match expression : " "{}, expected {} or sub/supertype".format( expr.type.name().camel, rtype.name().camel ) ) rtype = expr.type.unify(rtype) # This is the expression execution will reach if we have a bug in our # code (i.e. if matchers did not cover all cases). result = UnreachableExpr(rtype) # Wrap this "failing" expression with all the cases to match in the # appropriate order, so that in the end the first matchers are tested # first. for match_var, expr in reversed(constructed_matchers): casted = Cast.Expr(matched_expr, match_var.type, result_var=match_var) guard = Not.make_expr( Eq.make_expr(casted, LiteralExpr('null', casted.type)) ) if expr.type != rtype: # We already checked that type matches, so only way this is # true is if expr.type is an ASTNode type derived from # rtype. In that case, we need an explicity upcast. expr = Cast.Expr(expr, rtype) result = If.Expr(guard, expr, result, rtype) return result