Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
    def construct(self):
        """
        Construct a resolved expression for this.

        :rtype: EqExpr
        """
        cexpr = construct(self.expr)
        return self.construct_static(cexpr, abstract_expr=self)
Ejemplo n.º 6
0
    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()
Ejemplo n.º 7
0
    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
Ejemplo n.º 8
0
    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()
Ejemplo n.º 9
0
        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,
            )
Ejemplo n.º 10
0
    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)