예제 #1
0
    def create_iteration_var(
        self,
        existing_var: Optional[AbstractVariable],
        name_prefix: str,
        source_name: Optional[str] = None,
        type: Optional[CompiledType] = None
    ) -> AbstractVariable:
        """
        Create (when needed) an iteration variable and assign it (when
        provided) a source name.

        :param existing_var: Existing iteration variable. If provided, do not
            create a new variable.
        :param name_prefix: Prefix for the name of this variable in the
            generated code.
        :param source_name: If available, name of this variable in the DSL.
        :param type: Type for the variable.
        """
        if existing_var is None:
            result = AbstractVariable(
                names.Name(f"{name_prefix}_{next(self._counter)}"),
                type=type,
            )
        else:
            assert type == existing_var.type, (
                    f"Inconsistent type for {existing_var}: {type} and"
                    f" {existing_var.type}"
            )
            result = existing_var

        if result.source_name is None and source_name is not None:
            result.source_name = source_name

        return result
예제 #2
0
    def do_prepare(self):
        argspec = inspect.getargspec(self.expr_fn)

        check_multiple([
            (len(argspec.args)
             in (1, 2), 'Invalid collection iteration lambda: only one '
             'or two parameters expected'),
            (not argspec.varargs and not argspec.keywords,
             'Invalid collection iteration lambda: no *args or **kwargs'),
            (not argspec.defaults,
             'Invalid collection iteration lambda: No default values allowed '
             'for arguments')
        ])

        self.requires_index = len(argspec.args) == 2
        self.element_var = AbstractVariable(
            names.Name("Item_{}".format(next(CollectionExpression._counter))),
            source_name=names.Name.from_lower(argspec.args[0]))
        if self.requires_index:
            self.index_var = AbstractVariable(
                names.Name('I'),
                type=LongType,
                create_local=True,
                source_name=names.Name.from_lower(argspec.args[1]))
            expr = self.expr_fn(self.index_var, self.element_var)
        else:
            expr = self.expr_fn(self.element_var)
        self.expr = unsugar(expr)
예제 #3
0
    def do_prepare(self):
        # When this expression does not come from our Python DSL (see the
        # initialize method above), the sub-expression is ready to use: do not
        # try to expand the function.
        if self.expr is not None:
            return

        argspec = inspect.getargspec(self.expr_fn)

        check_multiple([
            (len(argspec.args) in (1, 2),
             'Invalid collection iteration lambda: only one '
             'or two parameters expected'),
            (not argspec.varargs and not argspec.keywords,
             'Invalid collection iteration lambda: no *args or **kwargs'),
            (not argspec.defaults,
             'Invalid collection iteration lambda: No default values allowed '
             'for arguments')
        ])

        if len(argspec.args) == 2:
            self.requires_index = True
            index_var_pos = 0
            item_var_pos = 1
        else:
            self.requires_index = False
            index_var_pos = None
            item_var_pos = 0

        # Get the name of the loop variable from the DSL. But don't when we
        # have a "default" one, such as for using the ".filter" combinator. In
        # this case, it's up to ".filter"'s special implementation to get the
        # name from the filter function.
        source_name = (None if self.expr_fn == collection_expr_identity else
                       names.Name.from_lower(argspec.args[item_var_pos]))

        self.element_var = AbstractVariable(
            names.Name("Item_{}".format(next(CollectionExpression._counter))),
            source_name=source_name
        )
        if self.requires_index:
            self.index_var = AbstractVariable(
                names.Name('I'), type=T.Int,
                source_name=names.Name.from_lower(argspec.args[index_var_pos])
            )
            expr = self.expr_fn(self.index_var, self.element_var)
        else:
            expr = self.expr_fn(self.element_var)
        self.expr = unsugar(expr)
예제 #4
0
    def do_prepare(self):
        argspec = inspect.getargspec(self.expr_fn)

        check_multiple([
            (len(argspec.args) in (1, 2),
             'Invalid collection iteration lambda: only one '
             'or two parameters expected'),
            (not argspec.varargs and not argspec.keywords,
             'Invalid collection iteration lambda: no *args or **kwargs'),
            (not argspec.defaults,
             'Invalid collection iteration lambda: No default values allowed '
             'for arguments')
        ])

        self.requires_index = len(argspec.args) == 2
        self.element_var = AbstractVariable(
            names.Name("Item_{}".format(next(CollectionExpression._counter))),
        )
        if self.requires_index:
            self.index_var = AbstractVariable(
                names.Name('I'), type=LongType,
                create_local=True
            )
            expr = self.expr_fn(self.index_var, self.element_var)
        else:
            expr = self.expr_fn(self.element_var)
        self.expr = assert_type(expr, AbstractExpression)
예제 #5
0
    def do_prepare(self):
        self.matchers = []

        for i, match_fn in enumerate(self.matchers_functions):
            argspec = inspect.getargspec(match_fn)
            check_source_language(
                len(argspec.args) == 1 and
                not argspec.varargs and
                not argspec.keywords and
                (not argspec.defaults or len(argspec.defaults) < 2),
                'Invalid matcher lambda'
            )

            if argspec.defaults:
                match_type = resolve_type(argspec.defaults[0])

                check_source_language(
                    issubclass(match_type, T.root_node)
                    or match_type.is_env_element_type,
                    'Invalid matching type: {}'.format(
                        match_type.name().camel
                    )
                )
            else:
                match_type = None

            match_var = AbstractVariable(
                names.Name('Match_{}'.format(i)),
                type=match_type,
                create_local=True,
                source_name=names.Name.from_lower(argspec.args[0])
            )
            self.matchers.append((match_type, match_var, match_fn(match_var)))
예제 #6
0
    def do_prepare(self):
        # If this Then was created using create_from exprs, there is no lambda
        # expansion to do.
        if self.then_expr:
            return

        argspec = inspect.getargspec(self.then_fn)
        check_source_language(
            len(argspec.args) == 1 and not argspec.varargs
            and not argspec.keywords and not argspec.defaults,
            'Invalid lambda for Then expression: exactly one parameter is'
            ' required, without a default value')

        self.var_expr = AbstractVariable(names.Name("Var_Expr"),
                                         create_local=True,
                                         source_name=names.Name(
                                             argspec.args[0]))
        self.then_expr = unsugar(self.then_fn(self.var_expr))
예제 #7
0
    def construct_common(self):
        """
        Construct the expressions commonly needed by collection expression
        subclasses, and return them as a tuple constituted of:

        1. The resolved collection expression.
        2. The resolved expression function passed to CollectionExpression's
           constructor.
        3. If the collection is an AST list, the iteration variable, whose type
           is the root grammar type. None otherwise.
        4. The element variable as a resolved expression. In the case of an AST
           list collection, this is just 3. converted to the specific type.
        5. The index variable as a resolved expression.
        6. The inner scope for the iteration.

        :rtype: (ResolvedExpression,
                 ResolvedExpression,
                 ResolvedExpression|None,
                 ResolvedExpression,
                 ResolvedExpression,
                 langkit.expressions.base.LocalVars.Scope)
        """
        collection_expr = construct(
            self.collection, lambda t: t.is_collection(),
            'Map cannot iterate on {expr_type}, which is not a collection')
        self.element_var.set_type(collection_expr.type.element_type())

        current_scope = PropertyDef.get_scope()

        # If we are iterating over an AST list, then we get root grammar typed
        # values. We need to convert them to the more specific type to get the
        # rest of the expression machinery work. For this, create a new
        # variable.
        if collection_expr.type.is_list_type:
            self.list_element_var = AbstractVariable(
                names.Name("List_Item_{}".format(
                    next(CollectionExpression._counter))),
                type=get_context().root_grammar_class)
            self.element_var.add_to_scope(current_scope)

        with current_scope.new_child() as iter_scope:
            if self.index_var:
                PropertyDef.get_scope().add(self.index_var.local_var)

            return (collection_expr, construct(self.expr), (construct(
                self.list_element_var) if self.list_element_var else None),
                    construct(self.element_var),
                    construct(self.index_var) if self.index_var else None,
                    iter_scope)
예제 #8
0
class Then(AbstractExpression):
    """
    Evaluate and return the result of `then_fn` if `expr` is not null.
    Otherwise, evaluate and return the result of `default_val`.

    For instance, to evaluate the property of a node or return false when this
    node is null::

        node.then(lambda n: n.my_property,
                  False)
    """
    class Expr(ComputingExpr):
        pretty_name = 'Then'

        def __init__(self,
                     expr,
                     var_expr,
                     then_expr,
                     default_expr,
                     then_scope,
                     abstract_expr=None):
            self.expr = expr
            self.var_expr = var_expr
            self.then_expr = then_expr
            self.default_expr = default_expr
            self.then_scope = then_scope
            self.static_type = self.then_expr.type

            super(Then.Expr, self).__init__('Result_Var',
                                            abstract_expr=abstract_expr)

        def _render_pre(self):
            return render('properties/then_ada', then=self)

        @property
        def subexprs(self):
            return {
                '0-prefix': self.expr,
                '1-then': self.then_expr,
                '2-default': self.default_expr
            }

        def _bindings(self):
            return [self.var_expr]

        def __repr__(self):
            return '<Then.Expr>'

    @staticmethod
    def create_from_exprs(base, then_expr, var_expr):
        """
        Create a Then expression without going through a lambda. Used
        internally to constructs then expressions for the underscore operator.
        """
        ret = Then(base, None)
        ret.then_expr = then_expr
        ret.var_expr = var_expr
        return ret

    def __init__(self, expr, then_fn, default_val=None):
        """
        :param AbstractExpression expr: The expression to use as a source for
            the then. Must be of a pointer type.
        :param (AbstractExpression) -> AbstractExpression then_fn: The
            function describing the expression to compute if expr is not null.
        :param AbstractExpression default_val: The expression to use as
            fallback if expr is null.
        """
        super(Then, self).__init__()
        self.expr = expr
        self.then_fn = then_fn
        self.default_val = default_val
        self.var_expr = self.then_expr = None
        self.underscore_then = False

    def do_prepare(self):
        # If this Then was created using create_from exprs, there is no lambda
        # expansion to do.
        if self.then_expr:
            return

        argspec = inspect.getargspec(self.then_fn)
        check_source_language(
            len(argspec.args) == 1 and not argspec.varargs
            and not argspec.keywords and not argspec.defaults,
            'Invalid lambda for Then expression: exactly one parameter is'
            ' required, without a default value')

        self.var_expr = AbstractVariable(names.Name("Var_Expr"),
                                         create_local=True,
                                         source_name=names.Name(
                                             argspec.args[0]))
        self.then_expr = unsugar(self.then_fn(self.var_expr))

    def construct(self):
        # Accept as a prefix:
        #
        #   * any pointer, since it can be checked against "null";
        #   * any StructType, since structs are nullable;
        #   * any LexicalEnvType, which has EmptyEnv as a null value.
        expr = construct(
            self.expr, lambda cls:
            (cls.is_ptr or cls.is_struct_type or cls.is_lexical_env_type),
            'Invalid prefix type for .then: {expr_type}')
        self.var_expr.set_type(expr.type)

        # Create a then-expr specific scope to restrict the span of the "then"
        # variable in the debugger.
        with PropertyDef.get_scope().new_child() as then_scope:
            then_scope.add(self.var_expr.local_var)
            then_expr = construct(self.then_expr)
            var_expr = construct(self.var_expr)
        then_expr = BindingScope(then_expr, [var_expr], scope=then_scope)

        # Affect default value to the fallback expression
        if self.default_val is None:
            check_source_language(
                then_expr.type.null_allowed or then_expr.type is T.BoolType,
                "Then expression should have a default value provided,"
                " in cases where the provided function's return type (here"
                " {}) does not have a default null value".format(
                    then_expr.type.dsl_name))
            default_expr = construct(No(then_expr.type))
        else:
            default_expr = construct(self.default_val, then_expr.type)

        return Then.Expr(expr, construct(self.var_expr), then_expr,
                         default_expr, then_scope)

    def __repr__(self):
        return "<Then {}: {} {}>".format(self.expr, self.var_expr,
                                         self.then_expr)
예제 #9
0
    def construct_common(self) -> CollectionExpression.ConstructCommonResult:
        """
        Construct and return the expressions commonly needed by collection
        expression subclasses.
        """
        assert self.element_var is not None

        current_scope = PropertyDef.get_scope()

        # Because of the discrepancy between the storage type in list nodes
        # (always root nodes) and the element type that user code deals with
        # (non-root list elements and/or entities), we may need to introduce
        # variables and initializing expressions. This is what the code below
        # does.

        # First, build the collection expression. From the result, we can
        # deduce the type of the user element variable.
        collection_expr = construct(self.collection)

        # If the collection is actually an entity, unwrap the bare list node
        # and save the entity info for later.
        with_entities = collection_expr.type.is_entity_type
        if with_entities:
            saved_entity_coll_expr, collection_expr, entity_info = (
                collection_expr.destructure_entity()
            )
            collection_expr = SequenceExpr(saved_entity_coll_expr,
                                           collection_expr)

        check_source_language(
            collection_expr.type.is_collection,
            'Cannot iterate on {}, which is not a collection'.format(
                collection_expr.type.dsl_name
            )
        )

        # Now that potential entity types are unwrapped, we can look for its
        # element type.
        elt_type = collection_expr.type.element_type
        if with_entities:
            elt_type = elt_type.entity
        self.element_var.set_type(elt_type)
        user_element_var = construct(self.element_var)

        # List of element variables, and the associated initialization
        # expressions (when applicable).
        #
        # Start with the only element variable that exists at this point: the
        # one that the user code for each iteration uses directly. When
        # relevant, each step in the code below creates a new variable N and
        # initialize variable N-1 from it.
        element_vars: List[InitializedVar] = [InitializedVar(user_element_var)]

        # Node lists contain bare nodes: if the user code deals with entities,
        # create a variable to hold a bare node and initialize the user
        # variable using it.
        if with_entities:
            entity_var = element_vars[-1]
            node_var = AbstractVariable(
                names.Name('Bare') + self.element_var._name,
                type=elt_type.element_type
            )
            entity_var.init_expr = make_as_entity(
                construct(node_var), entity_info=entity_info
            )
            element_vars.append(InitializedVar(construct(node_var)))

        # Node lists contain root nodes: if the user code deals with non-root
        # nodes, create a variable to hold the root bare node and initialize
        # the non-root node using it.
        if (
            collection_expr.type.is_list_type
            and not collection_expr.type.is_root_node
        ):
            typed_elt_var = element_vars[-1]
            untyped_elt_var = AbstractVariable(
                names.Name('Untyped') + self.element_var._name,
                type=get_context().root_grammar_class
            )
            typed_elt_var.init_expr = UncheckedCastExpr(
                construct(untyped_elt_var), typed_elt_var.var.type
            )
            element_vars.append(InitializedVar(construct(untyped_elt_var)))

        # Keep track of the ultimate "codegen" element variable. Unlike all
        # other iteration variable, it is the only one that will be defined by
        # the "for" loop in Ada (the other ones must be declared as regular
        # local variables).
        codegen_element_var = element_vars[-1].var

        # Create a scope to contain the code that runs during an iteration and
        # lower the iteration expression.
        with current_scope.new_child() as inner_scope:
            inner_expr = construct(self.expr)

        # Build the list of all iteration variables
        iter_vars = list(element_vars)
        index_var = None
        if self.index_var:
            index_var = construct(self.index_var)
            iter_vars.append(InitializedVar(index_var))

        # Create local variables for all iteration variables that need it
        for v in iter_vars:
            if v.var != codegen_element_var:
                v.var.abstract_var.create_local_variable(inner_scope)

        return self.ConstructCommonResult(
            collection_expr,
            codegen_element_var,
            user_element_var,
            index_var,
            iter_vars,
            inner_expr,
            inner_scope,
        )
예제 #10
0
from langkit import names
from langkit.compiled_types import AnalysisUnitKind, AnalysisUnitType, T
from langkit.diagnostics import check_source_language
from langkit.expressions.base import (AbstractVariable, FieldAccessExpr,
                                      PropertyDef, ResolvedExpression,
                                      auto_attr, construct, render)

UnitSpecification = AbstractVariable(names.Name('Unit_Specification'),
                                     type=AnalysisUnitKind)
UnitBody = AbstractVariable(names.Name('Unit_Body'), type=AnalysisUnitKind)


class AnalysisUnitRoot(ResolvedExpression):
    """
    Construct that takes an analysis unit and that returns its root node.
    """
    def __init__(self, unit_expr):
        super(AnalysisUnitRoot, self).__init__()

        self.static_type = T.root_node
        self.unit_expr = unit_expr
        self.prefix_var = PropertyDef.get().vars.create(
            'Unit', self.unit_expr.type)

    def _render_pre(self):
        from langkit.compile_context import get_context
        return '{}\n{}'.format(
            self.unit_expr.render_pre(),
            render('properties/null_safety_check_ada',
                   expr=self.unit_expr,
                   result_var=self.prefix_var)
예제 #11
0
파일: boolean.py 프로젝트: AdaCore/langkit
 def do_prepare(self):
     self.var_expr = AbstractVariable(names.Name("Var_Expr"),
                                      create_local=True)
     self.then_expr = self.then_fn(self.var_expr)
예제 #12
0
파일: boolean.py 프로젝트: AdaCore/langkit
class Then(AbstractExpression):
    """
    Expression for the then boolean combinator that works as follows::

        expression.then(
            lambda expr_result: arbitrary_expression, default_expression
        )

    This property code will evaluate the arbitrary expression if expression
    evaluates to a not null result, and will evaluate the default_expression
    otherwise.
    """

    class Expr(ResolvedExpression):
        def __init__(self, expr, var_expr, then_expr, default_expr):
            self.expr = expr
            self.var_expr = var_expr
            self.then_expr = then_expr
            self.default_expr = default_expr
            self.static_type = self.then_expr.type
            self.result_var = PropertyDef.get().vars.create("Result_Var",
                                                            self.type)

            super(Then.Expr, self).__init__()

        def _render_pre(self):
            return render('properties/then_ada', then=self)

        def _render_expr(self):
            return self.result_var.name.camel_with_underscores

        def __repr__(self):
            return '<Then.Expr>'

    def __init__(self, expr, then_fn, default_val=None):
        """
        :param AbstractExpression expr: The expression to use as a source for
            the then. Must be of a pointer type.
        :param (AbstractExpression) -> AbstractExpression then_fn: The
            function describing the expression to compute if expr is not null.
        :param AbstractExpression default_val: The expression to use as
            fallback if expr is null.
        """
        super(Then, self).__init__()
        self.expr = expr
        self.then_fn = then_fn
        self.default_val = default_val
        self.var_expr = self.then_expr = None

    def do_prepare(self):
        self.var_expr = AbstractVariable(names.Name("Var_Expr"),
                                         create_local=True)
        self.then_expr = self.then_fn(self.var_expr)

    def construct(self):
        # Add var_expr to the scope for this Then expression
        PropertyDef.get_scope().add(self.var_expr.local_var)

        # Accept as a prefix:
        # * any pointer, since it can be checked against "null";
        # * any Struct, since its "Is_Null" field can be checked.
        expr = construct(self.expr,
                         lambda cls: cls.is_ptr or issubclass(cls, Struct))
        self.var_expr.set_type(expr.type)

        then_expr = construct(self.then_expr)

        # Affect default value to the fallback expression. For the moment,
        # only booleans and structs are handled.
        if self.default_val is None:
            if then_expr.type.matches(BoolType):
                default_expr = construct(False)
            elif issubclass(then_expr.type, Struct):
                default_expr = construct(No(
                    # Because we're doing issubclass instead of isinstance,
                    # PyCharm do not understand that then_exp.type is a Struct,
                    # so the following is necessary not to have warnings.
                    assert_type(then_expr.type, Struct)
                ))
            elif then_expr.type.matches(LexicalEnvType):
                default_expr = construct(EmptyEnv)
            else:
                # The following is not actually used but PyCharm's typer
                # requires it.
                default_expr = None

                check_source_language(
                    False,
                    "Then expression should have a default value provided, "
                    "in cases where the provided function's return type is "
                    "not Bool, here {}".format(then_expr.type.name().camel)
                )
        else:
            default_expr = construct(self.default_val, then_expr.type)

        return Then.Expr(expr, construct(self.var_expr), then_expr,
                         default_expr)
예제 #13
0
    check_source_language(
        p._uses_entity_info is not False,
        'This property has been explicitly tagged as not using entity info, so'
        ' .as_entity is invalid here')

    # We want to keep original type of node, so no downcast
    node_expr = construct(node, T.root_node, downcast=False)

    ret = make_as_entity(node_expr, abstract_expr=self)
    ret.create_result_var('Ent')
    return ret


@auto_attr
def as_bare_entity(self, node):
    """
    Wrap `node` into an entity, using default entity information (in
    particular, no rebindings).
    """
    node_expr = construct(node, T.root_node, downcast=False)
    ret = make_as_entity(node_expr,
                         entity_info=NullExpr(T.entity_info),
                         abstract_expr=self)
    ret.create_result_var('Ent')
    return ret


EmptyEnv = AbstractVariable(names.Name("AST_Envs.Empty_Env"),
                            type=T.LexicalEnv)
예제 #14
0
class CollectionExpression(AbstractExpression):
    """
    Base class to provide common code for abstract expressions working on
    collections.
    """

    _counter = count()

    def __init__(self, collection, expr):
        """
        :param AbstractExpression collection: Collection on which this map
            operation works.

        :param expr: Function that takes the induction variable and returns an
            expression to evaluate for each item in "collection". If the
            function takes two parameters, the first one will also be the
            an induction variable for the iteration index.
        :type collection: AbstractExpression
        """
        super(CollectionExpression, self).__init__()
        self.collection = collection
        self.expr_fn = expr
        self.expr = None
        self.element_var = None
        self.requires_index = False
        self.index_var = None

    def do_prepare(self):
        argspec = inspect.getargspec(self.expr_fn)

        check_multiple([
            (len(argspec.args) in (1, 2),
             'Invalid collection iteration lambda: only one '
             'or two parameters expected'),
            (not argspec.varargs and not argspec.keywords,
             'Invalid collection iteration lambda: no *args or **kwargs'),
            (not argspec.defaults,
             'Invalid collection iteration lambda: No default values allowed '
             'for arguments')
        ])

        self.requires_index = len(argspec.args) == 2
        self.element_var = AbstractVariable(
            names.Name("Item_{}".format(next(CollectionExpression._counter))),
        )
        if self.requires_index:
            self.index_var = AbstractVariable(
                names.Name('I'), type=LongType,
                create_local=True
            )
            expr = self.expr_fn(self.index_var, self.element_var)
        else:
            expr = self.expr_fn(self.element_var)
        self.expr = assert_type(expr, AbstractExpression)

    def construct_common(self):
        """
        Construct the expressions commonly needed by collection expression
        subclasses, and return them as a tuple constituted of:

        1. The resolved collection expression.
        2. The resolved expression function passed to CollectionExpression's
           constructor.
        3. The element variable as a resolved expression.
        4. The index variable as a resolved expression.
        5. The inner scope for the iteration.

        :rtype: (ResolvedExpression, ResolvedExpression,
                 ResolvedExpression, ResolvedExpression,
                 langkit.expressions.base.LocalVars.Scope)
        """
        collection_expr = construct(
            self.collection, lambda t: t.is_collection(),
            'Map cannot iterate on {expr_type}, which is not a collection'
        )

        with PropertyDef.get_scope().new_child() as iter_scope:
            if self.index_var:
                PropertyDef.get_scope().add(self.index_var.local_var)
            self.element_var.set_type(collection_expr.type.element_type())

            return (collection_expr,
                    construct(self.expr),
                    construct(self.element_var),
                    construct(self.index_var) if self.index_var else None,
                    iter_scope)
예제 #15
0
    def construct_common(self):
        """
        Construct and return the expressions commonly needed by collection
        expression subclasses.

        :rtype: CollectionExpression.ConstructCommonResult
        """
        current_scope = PropertyDef.get_scope()

        # First, build the collection expression. From the result, we can
        # deduce the type of the element variable.
        collection_expr = construct(self.collection)
        with_entities = collection_expr.type.is_entity_type
        if with_entities:
            saved_entity_coll_expr, collection_expr, entity_info = (
                collection_expr.destructure_entity())
            collection_expr = SequenceExpr(saved_entity_coll_expr,
                                           collection_expr)

        check_source_language(
            collection_expr.type.is_collection,
            'Cannot iterate on {}, which is not a collection'.format(
                collection_expr.type.dsl_name))

        elt_type = collection_expr.type.element_type
        if with_entities:
            elt_type = elt_type.entity
        self.element_var.set_type(elt_type)

        # List of "element" iteration variables
        elt_vars = [construct(self.element_var)]

        # List of initializing expressions for them
        elt_var_inits = []

        if with_entities:
            entity_var = elt_vars[-1]
            node_var = AbstractVariable(names.Name('Bare') +
                                        self.element_var._name,
                                        type=elt_type.element_type)
            elt_var_inits.append(
                make_as_entity(construct(node_var), entity_info=entity_info))
            elt_vars.append(construct(node_var))

        # If we are iterating over an AST list, then we get root grammar typed
        # values. We need to convert them to the more specific type to get the
        # rest of the expression machinery work.
        if collection_expr.type.is_list_type:
            typed_elt_var = elt_vars[-1]
            untyped_elt_var = AbstractVariable(
                names.Name('Untyped') + self.element_var._name,
                type=get_context().root_grammar_class)
            # Initialize the former last variable with a cast from the new last
            # variable and push the new last variable.
            elt_var_inits.append(
                UncheckedCastExpr(construct(untyped_elt_var),
                                  typed_elt_var.type))
            elt_vars.append(construct(untyped_elt_var))

        # Only then we can build the inner expression
        with current_scope.new_child() as inner_scope:
            inner_expr = construct(self.expr)

        if with_entities:
            entity_var.abstract_var.create_local_variable(inner_scope)
        if collection_expr.type.is_list_type:
            typed_elt_var.abstract_var.create_local_variable(inner_scope)

        if self.index_var:
            self.index_var.add_to_scope(inner_scope)

        elt_var_inits.append(None)

        return self.ConstructCommonResult(
            collection_expr, funcy.lzip(elt_vars, elt_var_inits),
            construct(self.index_var) if self.index_var else None, inner_expr,
            inner_scope)
예제 #16
0
class Then(AbstractExpression):
    """
    Expression for the then boolean combinator that works as follows::

        expression.then(
            lambda expr_result: arbitrary_expression, default_expression
        )

    This property code will evaluate the arbitrary expression if expression
    evaluates to a not null result, and will evaluate the default_expression
    otherwise.
    """
    class Expr(ResolvedExpression):
        pretty_name = 'Then'

        def __init__(self, expr, var_expr, then_expr, default_expr):
            self.expr = expr
            self.var_expr = var_expr
            self.then_expr = then_expr
            self.default_expr = default_expr
            self.static_type = self.then_expr.type
            self.result_var = PropertyDef.get().vars.create(
                "Result_Var", self.type)

            super(Then.Expr, self).__init__()

        def _render_pre(self):
            return render('properties/then_ada', then=self)

        def _render_expr(self):
            return self.result_var.name.camel_with_underscores

        @property
        def subexprs(self):
            return {
                '0-prefix': self.expr,
                '1-then': self.then_expr,
                '2-default': self.default_expr
            }

        def _bindings(self):
            return [self.var_expr]

        def __repr__(self):
            return '<Then.Expr>'

    @staticmethod
    def create_from_exprs(base, then_expr, var_expr):
        """
        Create a Then expression without going through a lambda. Used
        internally to constructs then expressions for the underscore operator.
        """
        ret = Then(base, None)
        ret.then_expr = then_expr
        ret.var_expr = var_expr
        return ret

    def __init__(self, expr, then_fn, default_val=None):
        """
        :param AbstractExpression expr: The expression to use as a source for
            the then. Must be of a pointer type.
        :param (AbstractExpression) -> AbstractExpression then_fn: The
            function describing the expression to compute if expr is not null.
        :param AbstractExpression default_val: The expression to use as
            fallback if expr is null.
        """
        super(Then, self).__init__()
        self.expr = expr
        self.then_fn = then_fn
        self.default_val = default_val
        self.var_expr = self.then_expr = None
        self.underscore_then = False

    def do_prepare(self):
        # If this Then was created using create_from exprs, there is no lambda
        # expansion to do.
        if self.then_expr:
            return

        argspec = inspect.getargspec(self.then_fn)
        check_source_language(
            len(argspec.args) == 1 and not argspec.varargs
            and not argspec.keywords and not argspec.defaults,
            'Invalid lambda for Then expression: exactly one parameter is'
            ' required, without a default value')

        self.var_expr = AbstractVariable(names.Name("Var_Expr"),
                                         create_local=True,
                                         source_name=names.Name(
                                             argspec.args[0]))
        self.then_expr = self.then_fn(self.var_expr)

    def construct(self):
        # Add var_expr to the scope for this Then expression
        PropertyDef.get_scope().add(self.var_expr.local_var)

        # Accept as a prefix:
        # * any pointer, since it can be checked against "null";
        # * any Struct, since its "Is_Null" field can be checked.
        expr = construct(self.expr,
                         lambda cls: cls.is_ptr or issubclass(cls, Struct))
        self.var_expr.set_type(expr.type)

        then_expr = construct(self.then_expr)

        # Affect default value to the fallback expression. For the moment,
        # only booleans and structs are handled.
        if self.default_val is None:
            if then_expr.type.matches(BoolType):
                default_expr = construct(False)
            elif issubclass(then_expr.type, Struct):
                default_expr = construct(
                    No(
                        # Because we're doing issubclass instead of isinstance,
                        # PyCharm do not understand that then_exp.type is a Struct,
                        # so the following is necessary not to have warnings.
                        assert_type(then_expr.type, Struct)))
            elif then_expr.type.matches(LexicalEnvType):
                default_expr = construct(EmptyEnv)
            elif then_expr.type.matches(Symbol):
                default_expr = LiteralExpr(Symbol.nullexpr(), Symbol)
            else:
                # The following is not actually used but PyCharm's typer
                # requires it.
                default_expr = None

                check_source_language(
                    False,
                    "Then expression should have a default value provided, "
                    "in cases where the provided function's return type is "
                    "not Bool, here {}".format(then_expr.type.name().camel))
        else:
            default_expr = construct(self.default_val, then_expr.type)

        return Then.Expr(expr, construct(self.var_expr), then_expr,
                         default_expr)

    def __repr__(self):
        return "<Then {}: {} {}>".format(self.expr, self.var_expr,
                                         self.then_expr)
예제 #17
0
class CollectionExpression(AbstractExpression):
    """
    Base class to provide common code for abstract expressions working on
    collections.
    """

    _counter = count()

    def __init__(self, collection, expr):
        """
        :param AbstractExpression collection: Collection on which this map
            operation works.

        :param expr: Function that takes the induction variable and returns an
            expression to evaluate for each item in "collection". If the
            function takes two parameters, the first one will also be the
            an induction variable for the iteration index.
        :type collection: AbstractExpression
        """
        super(CollectionExpression, self).__init__()
        self.collection = collection
        self.expr_fn = expr
        self.expr = None
        self.list_element_var = None
        self.element_var = None
        self.requires_index = False
        self.index_var = None

    def do_prepare(self):
        argspec = inspect.getargspec(self.expr_fn)

        check_multiple([
            (len(argspec.args)
             in (1, 2), 'Invalid collection iteration lambda: only one '
             'or two parameters expected'),
            (not argspec.varargs and not argspec.keywords,
             'Invalid collection iteration lambda: no *args or **kwargs'),
            (not argspec.defaults,
             'Invalid collection iteration lambda: No default values allowed '
             'for arguments')
        ])

        self.requires_index = len(argspec.args) == 2
        self.element_var = AbstractVariable(
            names.Name("Item_{}".format(next(CollectionExpression._counter))),
            source_name=names.Name.from_lower(argspec.args[0]))
        if self.requires_index:
            self.index_var = AbstractVariable(
                names.Name('I'),
                type=LongType,
                create_local=True,
                source_name=names.Name.from_lower(argspec.args[1]))
            expr = self.expr_fn(self.index_var, self.element_var)
        else:
            expr = self.expr_fn(self.element_var)
        self.expr = unsugar(expr)

    def construct_common(self):
        """
        Construct the expressions commonly needed by collection expression
        subclasses, and return them as a tuple constituted of:

        1. The resolved collection expression.
        2. The resolved expression function passed to CollectionExpression's
           constructor.
        3. If the collection is an AST list, the iteration variable, whose type
           is the root grammar type. None otherwise.
        4. The element variable as a resolved expression. In the case of an AST
           list collection, this is just 3. converted to the specific type.
        5. The index variable as a resolved expression.
        6. The inner scope for the iteration.

        :rtype: (ResolvedExpression,
                 ResolvedExpression,
                 ResolvedExpression|None,
                 ResolvedExpression,
                 ResolvedExpression,
                 langkit.expressions.base.LocalVars.Scope)
        """
        collection_expr = construct(
            self.collection, lambda t: t.is_collection(),
            'Map cannot iterate on {expr_type}, which is not a collection')
        self.element_var.set_type(collection_expr.type.element_type())

        current_scope = PropertyDef.get_scope()

        # If we are iterating over an AST list, then we get root grammar typed
        # values. We need to convert them to the more specific type to get the
        # rest of the expression machinery work. For this, create a new
        # variable.
        if collection_expr.type.is_list_type:
            self.list_element_var = AbstractVariable(
                names.Name("List_Item_{}".format(
                    next(CollectionExpression._counter))),
                type=get_context().root_grammar_class)
            self.element_var.add_to_scope(current_scope)

        with current_scope.new_child() as iter_scope:
            if self.index_var:
                PropertyDef.get_scope().add(self.index_var.local_var)

            return (collection_expr, construct(self.expr), (construct(
                self.list_element_var) if self.list_element_var else None),
                    construct(self.element_var),
                    construct(self.index_var) if self.index_var else None,
                    iter_scope)
예제 #18
0
class CollectionExpression(AbstractExpression):
    """
    Base class to provide common code for abstract expressions working on
    collections.
    """

    _counter = count()

    class ConstructCommonResult:
        """
        Holder for the result of the "construct_common" method.
        """
        def __init__(self, collection_expr, element_vars, index_var,
                     inner_expr, inner_scope):
            self.collection_expr = collection_expr
            """
            Resolved expression corresponding to the collection on which the
            iteration is done.

            :type: ResolvedExpression
            """

            self.element_vars = element_vars
            """
            List of "element" iteration variables, and their initialization
            expression, if any.

            we need a list of "element" iteration variables for code generation
            purposes. For instance, assuming we iterate on an entity that is an
            AST list, we need 3 variables::

              * one that contains the node whose type is the root one (AST
                lists contain only root nodes in the generated code);

              * one that contains the node that is casted to the proper type;

              * one that wraps this casted node as an entity.

            The first variable is the one used to expand iteration expressions
            (see the "user_element_var" property.  This is the one that must
            have a source name. The other ones are mere code generation
            temporaries.

            The last variable is the one that is used as the actual iteration
            variable in the generated code. This is the only one that will not
            require explicit initialization.

            :type: list[(ResolvedExpression, ResolvedExpression|None)]
            """

            self.index_var = index_var
            """
            The index variable as a resolved expression, if required.

            :type: ResolvedExpression|None
            """

            self.inner_expr = inner_expr
            """
            Resolved expression to be evaluated for each collection item.

            :type: ResolvedExpression
            """

            self.inner_scope = inner_scope
            """
            Local variable scope for the body of the iteration.

            :type: langkit.expressions.base.LocalVars.Scope
            """

    def __init__(self, collection, expr):
        """
        :param AbstractExpression collection: Collection on which this map
            operation works.

        :param expr: Function that takes the induction variable and returns an
            expression to evaluate for each item in "collection". If the
            function takes two parameters, the first one will also be the
            an induction variable for the iteration index.
        :type collection: AbstractExpression
        """
        super().__init__()
        self.collection = collection
        self.expr_fn = expr
        self.expr = None
        self.element_var = None
        self.requires_index = False
        self.index_var = None

    def initialize(self,
                   expr: AbstractExpression,
                   element_var: AbstractVariable,
                   index_var: Optional[AbstractVariable] = None) -> None:
        """
        Initialize this expression using already expanded sub-expressions.

        This is useful when building expressions with item sub-expressions
        outside of our Python DSL.
        """
        self.expr = expr
        self.element_var = element_var
        self.requires_index = index_var is not None
        self.index_var = index_var

    def do_prepare(self):
        # When this expression does not come from our Python DSL (see the
        # initialize method above), the sub-expression is ready to use: do not
        # try to expand the function.
        if self.expr is not None:
            return

        argspec = inspect.getargspec(self.expr_fn)

        check_multiple([
            (len(argspec.args)
             in (1, 2), 'Invalid collection iteration lambda: only one '
             'or two parameters expected'),
            (not argspec.varargs and not argspec.keywords,
             'Invalid collection iteration lambda: no *args or **kwargs'),
            (not argspec.defaults,
             'Invalid collection iteration lambda: No default values allowed '
             'for arguments')
        ])

        if len(argspec.args) == 2:
            self.requires_index = True
            index_var_pos = 0
            item_var_pos = 1
        else:
            self.requires_index = False
            index_var_pos = None
            item_var_pos = 0

        # Get the name of the loop variable from the DSL. But don't when we
        # have a "default" one, such as for using the ".filter" combinator. In
        # this case, it's up to ".filter"'s special implementation to get the
        # name from the filter function.
        source_name = (None if self.expr_fn == collection_expr_identity else
                       names.Name.from_lower(argspec.args[item_var_pos]))

        self.element_var = AbstractVariable(names.Name("Item_{}".format(
            next(CollectionExpression._counter))),
                                            source_name=source_name)
        if self.requires_index:
            self.index_var = AbstractVariable(
                names.Name('I'),
                type=T.Int,
                source_name=names.Name.from_lower(argspec.args[index_var_pos]))
            expr = self.expr_fn(self.index_var, self.element_var)
        else:
            expr = self.expr_fn(self.element_var)
        self.expr = unsugar(expr)

    def construct_common(self):
        """
        Construct and return the expressions commonly needed by collection
        expression subclasses.

        :rtype: CollectionExpression.ConstructCommonResult
        """
        current_scope = PropertyDef.get_scope()

        # First, build the collection expression. From the result, we can
        # deduce the type of the element variable.
        collection_expr = construct(self.collection)
        with_entities = collection_expr.type.is_entity_type
        if with_entities:
            saved_entity_coll_expr, collection_expr, entity_info = (
                collection_expr.destructure_entity())
            collection_expr = SequenceExpr(saved_entity_coll_expr,
                                           collection_expr)

        check_source_language(
            collection_expr.type.is_collection,
            'Cannot iterate on {}, which is not a collection'.format(
                collection_expr.type.dsl_name))

        elt_type = collection_expr.type.element_type
        if with_entities:
            elt_type = elt_type.entity
        self.element_var.set_type(elt_type)

        # List of "element" iteration variables
        elt_vars = [construct(self.element_var)]

        # List of initializing expressions for them
        elt_var_inits = []

        if with_entities:
            entity_var = elt_vars[-1]
            node_var = AbstractVariable(names.Name('Bare') +
                                        self.element_var._name,
                                        type=elt_type.element_type)
            elt_var_inits.append(
                make_as_entity(construct(node_var), entity_info=entity_info))
            elt_vars.append(construct(node_var))

        # If we are iterating over an AST list, then we get root grammar typed
        # values. We need to convert them to the more specific type to get the
        # rest of the expression machinery work.
        if collection_expr.type.is_list_type:
            typed_elt_var = elt_vars[-1]
            untyped_elt_var = AbstractVariable(
                names.Name('Untyped') + self.element_var._name,
                type=get_context().root_grammar_class)
            # Initialize the former last variable with a cast from the new last
            # variable and push the new last variable.
            elt_var_inits.append(
                UncheckedCastExpr(construct(untyped_elt_var),
                                  typed_elt_var.type))
            elt_vars.append(construct(untyped_elt_var))

        # Only then we can build the inner expression
        with current_scope.new_child() as inner_scope:
            inner_expr = construct(self.expr)

        if with_entities:
            entity_var.abstract_var.create_local_variable(inner_scope)
        if collection_expr.type.is_list_type:
            typed_elt_var.abstract_var.create_local_variable(inner_scope)

        if self.index_var:
            self.index_var.add_to_scope(inner_scope)

        elt_var_inits.append(None)

        return self.ConstructCommonResult(
            collection_expr, funcy.lzip(elt_vars, elt_var_inits),
            construct(self.index_var) if self.index_var else None, inner_expr,
            inner_scope)
예제 #19
0
    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])
        )