def construct(self): """ Construct a resolved expression that is the result of testing the kind of a node. :rtype: IsAExpr """ expr = construct(self.expr) as_entity = expr.type.is_entity_type def resolve(astnode): t = resolve_type(astnode) return t.entity if as_entity and t.is_ast_node else t astnodes = [resolve(a) for a in self.astnodes] for a in astnodes: check_source_language( a.is_ast_node or a.is_entity_type, "Expected ASTNode subclass or entity, got {}".format(a)) check_source_language( a.matches(expr.type), ('When testing the dynamic subtype of an AST node, the type to' ' check must be a subclass of the value static type. Here, {}' ' is not a subclass of {}.'.format(a.dsl_name, expr.type.dsl_name))) return IsA.Expr(expr, astnodes, abstract_expr=self)
def construct(self): """ Construct a resolved expression that is the result of casting a AST node. :rtype: Cast.Expr """ expr = construct(self.expr) t = expr.type dest_type = (self.dest_type.entity if t.is_entity_type and not self.dest_type.is_entity_type else self.dest_type) check_source_language( dest_type.matches(t) or t.matches(dest_type), 'Cannot cast {} to {}: only (up/down)casting is ' 'allowed'.format(t.dsl_name, dest_type.dsl_name)) check_source_language(expr.type != dest_type, 'Casting to the same type', severity=Severity.warning) return Cast.Expr(expr, dest_type, do_raise=self.do_raise, abstract_expr=self)
def resolve_field(self): """ Resolve the field that should be accessed, by: - Constructing the receiver; - Getting its corresponding field. :rtype: AbstractNodeData """ self.receiver_expr = construct(self.receiver) pfx_type = self.receiver_expr.type check_source_language( pfx_type.is_base_struct_type, '{} values have no field (accessed field was {})'.format( pfx_type.dsl_name, self.field)) self.to_get = pfx_type.get_abstract_node_data_dict().get( self.field, None) ":type: AbstractNodeField" # If still not found, maybe the receiver is an entity, in which case we # want to do implicit dereference. if not self.to_get and pfx_type.is_entity_type: self.to_get = ( pfx_type.element_type.get_abstract_node_data_dict().get( self.field, None)) self.is_deref = bool(self.to_get) return self.to_get
def construct(self): """ Construct a resolved expression for this. :rtype: ResolvedExpression """ outer_scope = PropertyDef.get_scope() matched_expr = construct( self.matched_expr, lambda t: t.is_ast_node or t.is_entity_type, 'Match expressions can only work on AST nodes or entities: got' ' {expr_type} instead') is_entity = matched_expr.type.is_entity_type 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 is_entity and typ and not typ.is_entity_type: typ = typ.entity var._type = typ var.local_var.type = typ if typ is not None: check_source_language( typ.matches(matched_expr.type), 'Cannot match {} (input type is {})'.format( typ.dsl_name, matched_expr.type.dsl_name)) else: # The default matcher (if any) matches the most general type, # which is the input type. var.set_type(matched_expr.type) # Create a scope so that match_var is contained in this branch as # is not exposed outside in the debug info. with outer_scope.new_child() as inner_scope: inner_scope.add(var.local_var) matchers.append( Match.Matcher(construct(var), construct(expr), inner_scope)) # * all possible input types must have at least one matcher. Also warn # if some matchers are unreachable. self._check_match_coverage(matched_expr.type) return Match.Expr(matched_expr, matchers, abstract_expr=self)
def construct(self): """ Construct a resolved expression for this. :rtype: EqExpr """ cexpr = construct(self.expr) return self.construct_static(cexpr, abstract_expr=self)
def _render_field_access(self, p): """ Helper to render a simple field access to the property P in the context of an environment specification. :param PropertyDef p: The property to access. It must accept no explicit argument. :rtype: str """ assert not p.natural_arguments with PropertyDef.bind_none(), \ Self.bind_type(self.ast_node): return FieldAccess.Expr(construct(Self), p, []).render_expr()
def construct(self): """ Constructs the resolved expression corresponding to this field access. It can be either a field access or a property call. :rtype: FieldAccessExpr """ to_get = self.resolve_field() # If still not found, we have a problem check_source_language( to_get is not None, "Type {} has no '{}' field or property".format( self.receiver_expr.type.dsl_name, self.field)) check_source_language( not to_get.is_internal, '{} is for internal use only'.format(to_get.qualname)) # Check that this property actually accepts these arguments and that # they are correctly typed. input_args = self.arguments or FieldAccess.Arguments([], {}) args = input_args.associate(to_get) assert len(args) == len(to_get.natural_arguments) arg_exprs = [ None if actual is None else construct( actual, formal.type, custom_msg='Invalid "{}" actual{} for {}:'.format( formal.name.lower, ' (#{})'.format(key) if isinstance(key, int) else '', to_get.qualname, ) + ' expected {expected} but got {expr_type}') for (key, actual), formal in zip(args, to_get.natural_arguments) ] # Even though it is redundant with DynamicVariable.construct, check # that the callee's dynamic variables are bound here so we can emit a # helpful error message if that's not the case. if isinstance(self.to_get, PropertyDef): DynamicVariable.check_call_bindings(self.to_get, 'In call to {prop}') ret = FieldAccess.Expr(self.receiver_expr, to_get, arg_exprs, self.is_deref, abstract_expr=self) return ret
def _render_field_access(self, p): """ Helper to render a simple field access to the property P in the context of an environment specification. :param PropertyDef p: The property to access. It must accept no explicit argument. :rtype: str """ assert not p.explicit_arguments with PropertyDef.bind_none(), \ Self.bind_type(self.ast_node), \ Env.bind_name('Current_Env'): return FieldAccess.Expr(construct(Self), p, []).render_expr()
def __init__(self, receiver_expr, node_data, arguments, implicit_deref=False, unsafe=False, abstract_expr=None): """ :param ResolvedExpression receiver_expr: The receiver of the field access. :param langkit.compiled_types.AbstracNodeData node_data: The accessed property or field. :param list[ResolvedExpression|None] arguments: If non-empty, this field access will actually be a primitive call. Each item is a ResolvedExpression for an actual to pass, or None for arguments to let them have their default value. List list must have the same size as `node_data.natural_arguments`. :param bool implicit_deref: Whether the receiver is an entity, and we want to access a field or property of the stored node. In the case of an entity prefix for an AST node field, return an entity with the same entity info. :param bool unsafe: If true, don't generate the null crheck before doing the field access. This is used to avoid noisy and useless null checks in generated code: these checks would fail only because of a bug in the code generator. :param AbstractExpression|None abstract_expr: See ResolvedExpression's constructor. """ # When calling environment properties, the call itself happens are # outside a property. We cannot create a variable in this context, # and the field access is not supposed to require a "render_pre" # step. p = PropertyDef.get() self.simple_field_access = not p assert not self.simple_field_access or not implicit_deref self.implicit_deref = implicit_deref self.unsafe = unsafe self.original_receiver_expr = receiver_expr self.receiver_expr = (receiver_expr if self.simple_field_access or self.unsafe else NullCheckExpr( receiver_expr, implicit_deref)) self.node_data = node_data # Keep the original node data for debugging purposes self.original_node_data = node_data # If this is a property call, take the root property if self.node_data.is_property: self.node_data = self.node_data.root_property self.arguments = arguments if self.arguments is not None: assert (len(self.arguments) == len( self.node_data.natural_arguments)) if isinstance(self.node_data, PropertyDef): self.dynamic_vars = [ construct(dynvar) for dynvar in self.node_data.dynamic_vars ] self.static_type = self.node_data.type if self.wrap_result_in_entity: self.static_type = self.static_type.entity # Create a variable for all field accesses in properties. This is # needed because the property will return an owning reference, so # we need it to be attached to the scope. In other cases, this can # make debugging easier. super(FieldAccess.Expr, self).__init__( None if self.simple_field_access else 'Fld', abstract_expr=abstract_expr, )
def construct(self): """ :rtype: StructExpr """ if self.struct_type.is_ast_node: check_source_language( not self.struct_type.is_list_type, 'List node synthetization is not supported for now') check_source_language( PropertyDef.get().memoized, 'Node synthetization can only happen inside a memoized' ' property') # Make sure the provided set of fields matches the one the struct # needs. def error_if_not_empty(name_set, message): check_source_language( not name_set, ('{}: {}'.format(message, ', '.join(name for name in name_set)))) # Create a dict of field names to fields in the struct type def is_required(f): if isinstance(f, BuiltinField): # BuiltinFields are actually stored fields only for structure # types (not for nodes). return self.struct_type.is_struct_type elif isinstance(f, Field): return not f.null else: return isinstance(f, UserField) required_fields = { f._name.lower: f for f in self.struct_type.get_abstract_node_data() if is_required(f) } error_if_not_empty( set(required_fields) - set(self.field_values.keys()), 'Values are missing for {} fields'.format( self.struct_type.dsl_name)) error_if_not_empty( set(self.field_values.keys()) - set(required_fields), 'Extraneous fields for {}'.format(self.struct_type.dsl_name)) # At this stage, we know that the user has only provided fields that # are valid for the struct type. provided_fields = { required_fields[name].name: construct( value, required_fields[name].type, 'Wrong type for field {}: expected {{expected}}, ' 'got {{expr_type}}'.format(name)) for name, value in self.field_values.items() } expr_cls = (New.NodeExpr if self.struct_type.is_ast_node else New.StructExpr) return expr_cls(self.struct_type, provided_fields, abstract_expr=self)