def create_internal_property( self, name: str, expr: Optional[AbstractExpression], type: Optional[CompiledType]) -> Optional[PropertyDef]: """ Create an internal property for this env spec. If ``expr`` is None, do not create a property and return None. Otherwise, unsugar it. :param name: Lower-case name to use to create this property name. Since the property is internal, the name is decorated. """ assert self.ast_node is not None if expr is None: return None expr = unsugar(expr) p = PropertyDef(expr, AbstractNodeData.PREFIX_INTERNAL, name=names.Name('{}_{}'.format( name, next(self.PROPERTY_COUNT))), public=False, type=type, ignore_warn_on_node=True) p._indexing_name = '_{}'.format(p.original_name.lower) p._original_name = names.Name.from_lower(p._indexing_name) p.location = getattr(expr, 'location') or self.location self.ast_node.add_field(p) return p
def create_internal_property(self, name, expr, type): """ Create an internal property for this env spec. """ if expr is None: return None p = PropertyDef( expr, AbstractNodeData.PREFIX_INTERNAL, name=names.Name('_{}_{}'.format(name, next(self.PROPERTY_COUNT))), public=False, type=type, ignore_warn_on_node=True ) p.location = getattr(expr, 'location') or self.location self.ast_node.add_field(p) return p
def compute_properties(self, compile_only=False): """ Compute information related to ASTNode's properties. This needs to be a global analysis because we want to compute which properties need to be dispatching, and this is determined not only by the context of one node, but by whether the parent has a property with the same name. """ for pass_fn in PropertyDef.compilation_passes(compile_only): for astnode in self.astnode_types: for prop in astnode.get_properties(include_inherited=False): if self.verbosity.debug: print 'Running {} on {}'.format( pass_fn.__name__, prop.qualname, ) with prop.diagnostic_context(): pass_fn(prop) # Env specs generate properties, and have some invariants to # check after properties have been properly computed. Perform # these checks only once and at the proper time, though. if (pass_fn == PropertyDef.construct_and_type_expression and astnode.env_spec): astnode.env_spec.check_properties()
def _render_field_access(self, p: PropertyDef) -> str: """ Helper to render a simple field access to the property P in the context of an environment specification. :param p: The property to access. It must accept no explicit argument. """ assert not p.natural_arguments with PropertyDef.bind_none(), \ Self.bind_type(self.ast_node): return FieldAccess.Expr(Self.construct_nocheck(), p, []).render_expr()
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 create_internal_property(self, name, expr, type): """ Create an internal property for this env spec. If ``expr`` is None, do not create a property and return None. Otherwise, unsugar it. :param str name: Lower-case name to use to create this property name. Since the property is internal, the name is decorated. """ if expr is None: return None expr = unsugar(expr) p = PropertyDef(expr, AbstractNodeData.PREFIX_INTERNAL, name=names.Name('_{}_{}'.format( name, next(self.PROPERTY_COUNT))), public=False, type=type, ignore_warn_on_node=True) p.location = getattr(expr, 'location') or self.location self.ast_node.add_field(p) return p
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 compute_properties(self): """ Compute information related to ASTNode's properties. This needs to be a global analysis because we want to compute which properties need to be dispatching, and this is determined not only by the context of one node, but by whether the parent has a property with the same name. """ for pass_fn in PropertyDef.compilation_passes(): for astnode in self.astnode_types: for prop in astnode.get_properties(include_inherited=False): with prop.diagnostic_context(): pass_fn(prop) if astnode.env_spec: # Env specs generate properties, and have some invariants # to check after properties have been properly computed. astnode.env_spec.prepare()
def create_internal_property(name, expr, type): if expr is None: return None # Set has_implicit_env for these internal properties so that they # can use a default environment that the context gives. This # default will be the Self_Env of the parent node, which is always # the same, regardless of being run in the populate lexical env # pass or later on. See Initial_Env_Getter_Fn functions for the # code that fetches this default environment. p = PropertyDef(expr, AbstractNodeData.PREFIX_INTERNAL, name=names.Name('_{}_{}'.format( name, next(self.PROPERTY_COUNT))), private=True, type=type, has_implicit_env=True) result.append(p) return p
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)