def entity_info_expr(self): """ Return the value of the entity info parameter along, compute its value. Return None otherwise. :rtype: str|None """ # If the property that this field accesses requires entity info, # then the prefix is supposed to be an entity. There are only two # exceptions to this: either the entity info is actually optional, # either we are out of any property. In these cases, leave the # default entity info. # # See CompileCtx.compute_uses_entity_info_attr for how we check # that the assertion always holds. if not self.implicit_deref: assert (self.node_data.optional_entity_info or PropertyDef.get() is None) return None # When it is required, entity info comes from the entity, if we're # calling the property on an entity. return '{}.Info'.format(self.prefix)
def __init__(self, prefix_expr, matchers, abstract_expr=None): """ :param ResolvedExpression prefix_expr: The expression on which the dispatch occurs. It must be either an AST node or an entity. :param list[Match.Matcher] matchers: List of matchers for this node. """ assert (prefix_expr.type.is_ast_node or prefix_expr.type.is_entity_type) self.prefix_expr = NullCheckExpr( prefix_expr, implicit_deref=prefix_expr.type.is_entity_type) # Variable to hold the value of which we do dispatching # (prefix_expr). self.prefix_var = PropertyDef.get().vars.create( 'Match_Prefix', self.prefix_expr.type) # Compute the return type as the unification of all branches rtype = matchers[-1].match_expr.type for m in matchers: rtype = m.match_expr.type.unify( rtype, 'Mismatching types in Match expression: got {self} but' ' expected {other} or sub/supertype') self.static_type = rtype # Wrap each matcher expression so that all capture variables are # bound and initialized. self.matchers = [] for m in matchers: # Initialize match_var. Note that assuming the code generation # is bug-free, this cast cannot fail, so don't generate type # check boilerplate. let_expr = Let.Expr( [m.match_var], [ Cast.Expr(self.prefix_var.ref_expr, m.match_var.type, unsafe=True) ], # ... and cast this matcher's result to the Match result's # type, as required by OOP with access types in Ada. (m.match_expr if m.match_expr.type == rtype else Cast.Expr( m.match_expr, rtype))) # Now do the binding for static analysis and debugging self.matchers.append( Match.Matcher( m.match_var, BindingScope(let_expr, [], scope=m.inner_scope), m.inner_scope)) # Determine for each matcher the set of concrete AST nodes it can # actually match. prefix_type = self.prefix_expr.type if prefix_type.is_entity_type: prefix_type = prefix_type.element_type matched_types, remainder = collapse_concrete_nodes( (prefix_type.element_type if prefix_type.is_entity_type else prefix_type), [m.match_astnode_type for m in self.matchers]) assert not remainder for matcher, matched in zip(self.matchers, matched_types): matcher.matched_concrete_nodes = matched super(Match.Expr, self).__init__('Match_Result', abstract_expr=abstract_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)