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)
示例#2
0
    def construct(self) -> ResolvedExpression:
        array_1 = construct(self.array_1)
        array_2 = construct(self.array_2)

        # Handle strings as a special case
        if array_1.type.is_string_type:
            check_source_language(
                array_2.type.is_string_type,
                "String type expected, got {}".format(array_2.type.dsl_name)
            )
            return CallExpr(
                "Concat_Result", "Concat_String", T.String, [array_1, array_2],
                abstract_expr=self,
            )

        def check_array(typ: CompiledType) -> None:
            check_source_language(
                typ.is_array_type,
                "Expected array type, got {}".format(typ.dsl_name)
            )

        check_array(array_1.type)
        check_array(array_2.type)

        check_multiple([
            (array_1.type == array_2.type,
             "Got different array element types in concat: {} and {}".format(
                 array_1.type.element_type.dsl_name,
                 array_2.type.element_type.dsl_name
             )),
        ])

        return CallExpr('Concat_Result', 'Concat', array_1.type,
                        [array_1, array_2],
                        abstract_expr=self)
示例#3
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)
示例#4
0
文件: logic.py 项目: AdaCore/langkit
    def do_prepare(self):
        check_multiple([
            (self.bind_property.type.matches(T.root_node),
             "The property passed to bind must return a subtype "
             "of {}".format(T.root_node.name().camel)),

            (self.bind_property.struct.matches(T.root_node),
             "The property passed to bind must belong to a subtype "
             "of {}".format(T.root_node.name().camel))
        ])

        self.bind_property.do_generate_logic_binder()
示例#5
0
文件: logic.py 项目: AdaCore/langkit
    def do_prepare(self):
        check_multiple([
            (isinstance(self.pred_property, PropertyDef),
             "Needs a property reference, got {}".format(self.pred_property)),

            (self.pred_property.type.matches(BoolType),
             "The property passed to predicate must return a boolean, "
             "got {}".format(self.pred_property.type.name().camel)),

            (self.pred_property.struct.matches(T.root_node),
             "The property passed to bind must belong to a subtype "
             "of {}".format(T.root_node.name().camel))
        ])
示例#6
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)
    def construct(self):
        array_1 = construct(self.array_1)
        array_2 = construct(self.array_2)

        # TODO: We don't use the type param to construct because construct will
        # try to cast arrays to the base array type. Would be better if
        # construct handled that correctly.
        check_type(array_1.type, ArrayType)
        check_type(array_2.type, ArrayType)

        check_multiple([
            (array_1.type == array_2.type,
             "Got different array element types in concat: {} and {}".format(
                 array_1.type.element_type().name(),
                 array_2.type.element_type().name())),
        ])

        return BuiltinCallExpr("Concat", array_1.type, [array_1, array_2],
                               "Concat_Result")
示例#8
0
    def construct(self):
        array_1 = construct(self.array_1)
        array_2 = construct(self.array_2)

        # TODO: We don't use the type param to construct because construct will
        # try to cast arrays to the base array type. Would be better if
        # construct handled that correctly.
        check_type(array_1.type, ArrayType)
        check_type(array_2.type, ArrayType)

        check_multiple([
            (array_1.type == array_2.type,
             "Got different array element types in concat: {} and {}".format(
                 array_1.type.element_type().name(),
                 array_2.type.element_type().name()
             )),
        ])

        return BuiltinCallExpr(
            "Concat", array_1.type, [array_1, array_2], "Concat_Result"
        )
示例#9
0
    def construct(self):
        array_1 = construct(self.array_1)
        array_2 = construct(self.array_2)

        def check_array(typ):
            check_source_language(
                typ.is_array_type,
                "Expected array type, got {}".format(typ.dsl_name))

        check_array(array_1.type)
        check_array(array_2.type)

        check_multiple([
            (array_1.type == array_2.type,
             "Got different array element types in concat: {} and {}".format(
                 array_1.type.element_type.dsl_name,
                 array_2.type.element_type.dsl_name)),
        ])

        return CallExpr('Concat_Result',
                        'Concat',
                        array_1.type, [array_1, array_2],
                        abstract_expr=self)
示例#10
0
    def prepare_iter_function(
        self,
        what: str,
        expr_fn: Union[Callable[[AbstractExpression], AbstractExpression],
                       Callable[[AbstractExpression, AbstractExpression],
                                AbstractExpression]]
    ) -> AbstractExpression:
        """
        Validate an iteration function, whether it only takes the iteration
        element or also the iteration index.

        Return the abstract expression to evaluate for each iteration.

        :param what: Purpose of this function. Used for diagnostics.
        :param expr_fn: Function to prepare.
        """
        check_type(
            expr_fn,
            types.FunctionType,
            "{what} passed to a collection expression must be a lambda or a"
            " function"
        )

        argspec = inspect.getargspec(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')
        ])

        # Get source names for the iteration variables
        index_required = False
        index_varname: Optional[str] = None
        element_varname: Optional[str] = None
        if len(argspec.args) == 2:
            index_required = True
            self.requires_index = True
            index_varname = argspec.args[0]
            element_varname = argspec.args[1]
        else:
            element_varname = argspec.args[0]

        # We are interested in names from user sources: disregard names from
        # the default functions function we define here.
        if expr_fn in builtin_collection_functions:
            index_varname = None
            element_varname = None

        # Make sure we have an iteration element variable
        self.element_var = self.create_iteration_var(
            self.element_var, "Item", element_varname
        )

        # Expand the function. If the index is required, make sure we have an
        # iteration variable for it.
        if index_required:
            self.index_var = self.create_iteration_var(
                self.index_var, "Index", index_varname, T.Int
            )
            expr = expr_fn(self.index_var, self.element_var)  # type: ignore
        else:
            expr = expr_fn(self.element_var)  # type: ignore

        return unsugar(expr)
示例#11
0
    def construct(self):
        check_multiple([
            (self.pred_property.type.matches(T.Bool),
             'Predicate property must return a boolean, got {}'.format(
                 self.pred_property.type.dsl_name)),
            (self.pred_property.struct.matches(T.root_node),
             'Predicate property must belong to a subtype of {}'.format(
                 T.root_node.dsl_name)),
        ])

        # Separate logic variable expressions from extra argument expressions
        exprs = [construct(e) for e in self.exprs]
        logic_var_exprs, closure_exprs = funcy.lsplit_by(
            lambda e: e.type == T.LogicVar, exprs)
        check_source_language(
            len(logic_var_exprs) > 0, "Predicate instantiation should have at "
            "least one logic variable expression")
        check_source_language(
            all(e.type != T.LogicVar for e in closure_exprs),
            'Logic variable expressions should be grouped at the beginning,'
            ' and should not appear after non logic variable expressions')

        # Make sure this predicate will work on clean logic variables
        logic_var_exprs = [ResetLogicVar(expr) for expr in logic_var_exprs]

        # Compute the list of arguments to pass to the property (Self
        # included).
        args = (
            [Argument(names.Name('Self'), self.pred_property.struct.entity)] +
            self.pred_property.natural_arguments)

        # Then check that 1) all extra passed actuals match what the property
        # arguments expect and that 2) arguments left without an actual have a
        # default value.
        default_passed_args = 0
        for i, (expr, arg) in enumerate(zip_longest(exprs, args)):

            if expr is None:
                check_source_language(
                    arg.default_value is not None,
                    'Missing an actual for argument #{} ({})'.format(
                        i, arg.name.lower))
                default_passed_args += 1
                continue

            check_source_language(
                arg is not None,
                'Too many actuals: at most {} expected, got {}'.format(
                    len(args), len(exprs)))

            if expr.type == T.LogicVar:
                check_source_language(
                    arg.type.matches(T.root_node.entity),
                    "Argument #{} of predicate "
                    "is a logic variable, the corresponding property formal "
                    "has type {}, but should be a descendent of {}".format(
                        i, arg.type.dsl_name, T.root_node.entity.dsl_name))
            else:
                check_source_language(
                    expr.type.matches(arg.type), "Argument #{} of predicate "
                    "has type {}, should be {}".format(i, expr.type.dsl_name,
                                                       arg.type.dsl_name))

        DynamicVariable.check_call_bindings(self.pred_property,
                                            'In predicate property {prop}')

        # Append dynamic variables to embed their values in the closure
        closure_exprs.extend(
            construct(dynvar) for dynvar in self.pred_property.dynamic_vars)

        pred_id = self.pred_property.do_generate_logic_predicate(
            tuple(e.type for e in closure_exprs), default_passed_args)

        args = " ({})".format(', '.join(
            ["{}"
             for _ in range(len(closure_exprs))])) if closure_exprs else ""
        predicate_expr = untyped_literal_expr(
            f"Create_{pred_id}_Predicate{args}", operands=closure_exprs)

        return Predicate.Expr(self.pred_property,
                              pred_id,
                              logic_var_exprs,
                              predicate_expr,
                              abstract_expr=self)
示例#12
0
    def construct(self):
        from langkit.compile_context import get_context
        self.resolve_props()

        get_context().do_generate_logic_binder(self.conv_prop, self.eq_prop)

        # We have to wait for the construct pass for the following checks
        # because they rely on type information, which is not supposed to be
        # computed before this pass.
        if self.conv_prop:
            check_multiple([
                (self.conv_prop.type.matches(T.root_node.entity),
                 'Bind property must return a subtype of {}'.format(
                     T.root_node.entity.dsl_name)),
                (self.conv_prop.struct.matches(T.root_node),
                 'Bind property must belong to a subtype of {}'.format(
                     T.root_node.dsl_name)),
            ])

            DynamicVariable.check_call_bindings(self.conv_prop,
                                                "In Bind's conv_prop {prop}")

        # Those checks are run in construct, because we need the eq_prop to be
        # prepared already, which is not certain in do_prepare (order
        # dependent).

        if self.eq_prop:
            args = self.eq_prop.natural_arguments
            check_multiple([
                (self.eq_prop.type == T.Bool,
                 'Equality property must return boolean'),
                (self.eq_prop.struct.matches(T.root_node),
                 'Equality property must belong to a subtype of {}'.format(
                     T.root_node.dsl_name)),
                (len(args) == 1,
                 'Equality property: expected 1 argument, got {}'.format(
                     len(args))),
            ])

            other_type = args[0].type
            check_source_language(
                other_type.is_entity_type,
                "First arg of equality property should be an entity type")
            check_source_language(
                other_type.element_type == self.eq_prop.struct,
                "Self and first argument should be of the same type")

            DynamicVariable.check_call_bindings(self.eq_prop,
                                                "In Bind's eq_prop {prop}")

        cprop_uid = (self.conv_prop.uid if self.conv_prop else "Default")
        eprop_uid = (self.eq_prop.uid if self.eq_prop else "Default")

        if self.conv_prop:
            pred_func = Bind.Expr.dynamic_vars_to_holder(
                self.conv_prop, 'Logic_Converter_{}'.format(cprop_uid))
        else:
            pred_func = untyped_literal_expr('No_Logic_Converter_Default')

        # Left operand must be a logic variable. Make sure the resulting
        # equation will work on a clean logic variable.
        lhs = ResetLogicVar(construct(self.from_expr, T.LogicVar))

        # Second one can be either a logic variable or an entity (or an AST
        # node that is promoted to an entity).
        rhs = construct(self.to_expr)

        if rhs.type.matches(T.LogicVar):
            # For this operand too, make sure it will work on a clean logic
            # variable.
            rhs = ResetLogicVar(rhs)
        elif rhs.type.matches(T.root_node):
            from langkit.expressions import make_as_entity
            rhs = make_as_entity(rhs)
        else:
            check_source_language(
                rhs.type.matches(T.root_node.entity),
                'Right operand must be either a logic variable or an entity,'
                ' got {}'.format(rhs.type.dsl_name))

        # Because of Ada OOP typing rules, for code generation to work
        # properly, make sure the type of `rhs` is the root node entity.
        if (rhs.type.matches(T.root_node.entity)
                and rhs.type is not T.root_node.entity):
            from langkit.expressions import Cast
            rhs = Cast.Expr(rhs, T.root_node.entity)

        return Bind.Expr(self.conv_prop,
                         self.eq_prop,
                         cprop_uid,
                         eprop_uid,
                         lhs,
                         rhs,
                         pred_func,
                         abstract_expr=self)
示例#13
0
    def construct(self):
        # We have to wait for the construct pass for the following checks
        # because they rely on type information, which is not supposed to be
        # computed before this pass.
        if self.conv_prop:
            check_multiple([
                (self.conv_prop.type.matches(T.root_node),
                 "The property passed to bind must return a subtype "
                 "of {}".format(T.root_node.name().camel)),

                (self.conv_prop.struct.matches(T.root_node),
                 "The property passed to bind must belong to a subtype "
                 "of {}".format(T.root_node.name().camel))
            ])

        # Those checks are run in construct, because we need the eq_prop to be
        # prepared already, which is not certain in do_prepare (order
        # dependent).

        if self.eq_prop:
            args = self.eq_prop.explicit_arguments
            check_multiple([
                (self.eq_prop.type == BoolType,
                 "Equality property must return boolean"),

                (self.eq_prop.struct.matches(T.root_node),
                 "The equality property passed to bind must belong to a "
                 "subtype of {}".format(T.root_node.name().camel)),

                (len(args) == 1,
                 "Expected 1 argument for eq_prop, got {}".format(len(args))),

            ])
            check_source_language(
                args[0].type == self.eq_prop.struct,
                "Self and first argument should be of the same type"
            )

        cprop_uid = (self.conv_prop.uid if self.conv_prop else "Default")
        eprop_uid = (self.eq_prop.uid if self.eq_prop else "Default")
        pred_func = untyped_literal_expr(
            "Logic_Converter_{}'(Env => {})".format(
                cprop_uid, construct(Env).render_expr()
            )
            if self.conv_prop
            else "No_Logic_Converter_Default"
        )

        def construct_operand(op):
            from langkit.expressions import Cast, New
            expr = construct(op)

            check_source_language(

                expr.type == LogicVarType
                or expr.type.matches(T.root_node)
                or expr.type.matches(T.root_node.env_el()),

                "Operands to a logic bind operator should be either "
                "a logic variable or an ASTNode, got {}".format(expr.type)
            )

            if expr.type.matches(T.root_node.env_el()):
                if expr.type is not T.root_node.env_el():
                    expr = Cast.Expr(expr, T.root_node.env_el())
            elif expr.type.matches(T.root_node):
                # Cast the ast node type if necessary
                if expr.type is not T.root_node:
                    expr = Cast.Expr(expr, T.root_node)

                # If the expression is a root node, implicitly construct an
                # env_element from it.
                expr = New.StructExpr(T.root_node.env_el(), {
                    Name('El'): expr,
                    Name('MD'): LiteralExpr('<>', None),
                    Name('Parents_Bindings'): LiteralExpr('null', None)
                })

            return expr

        lhs = construct_operand(self.from_expr)
        rhs = construct_operand(self.to_expr)

        return BuiltinCallExpr(
            "Bind_{}_{}.Create".format(cprop_uid, eprop_uid),
            EquationType,
            [lhs, rhs, pred_func],
            "Bind_Result"
        )
示例#14
0
    def construct(self):
        check_multiple([
            (isinstance(self.pred_property, PropertyDef),
             "Needs a property reference, got {}".format(self.pred_property)),

            (self.pred_property.type.matches(BoolType),
             "The property passed to predicate must return a boolean, "
             "got {}".format(self.pred_property.type.name().camel)),

            (self.pred_property.struct.matches(T.root_node),
             "The property passed to bind must belong to a subtype "
             "of {}".format(T.root_node.name().camel))
        ])

        exprs = [construct(e) for e in self.exprs]

        prop_types = [self.pred_property.struct] + [
            a.type for a in self.pred_property.explicit_arguments
        ]

        # Separate logic variable expressions from extra argument expressions
        logic_var_exprs, closure_exprs = funcy.split_by(
            lambda e: e.type == LogicVarType, exprs
        )

        check_source_language(
            len(logic_var_exprs) > 0, "Predicate instantiation should have at "
            "least one logic variable expression"
        )

        check_source_language(
            all(e.type != LogicVarType for e in closure_exprs), "Logic "
            "variable expressions should be grouped at the beginning, and "
            "should not appear after non logic variable expressions"
        )

        for i, (expr, arg_type) in enumerate(zip(exprs, prop_types)):
            if expr.type == LogicVarType:
                check_source_language(
                    arg_type.matches(T.root_node), "Argument #{} of predicate "
                    "is a logic variable, the corresponding property formal "
                    "has type {}, but should be a descendent of {}".format(
                        i, arg_type.name().camel, T.root_node.name().camel
                    )
                )
            else:
                check_source_language(
                    expr.type.matches(arg_type), "Argument #{} of predicate "
                    "has type {}, should be {}".format(
                        i, expr.type.name().camel, arg_type.name().camel
                    )
                )

        pred_id = self.pred_property.do_generate_logic_predicate(*[
            e.type for e in closure_exprs
        ])

        closure_exprs.append(construct(Env))

        # Append the debug image for the predicate
        closure_exprs.append(LiteralExpr('"{}.{}"'.format(
            self.pred_property.name.camel_with_underscores,
            self.pred_property.struct.name().camel_with_underscores
        ), type=None))

        logic_var_exprs.append(
            BasicExpr("{}_Predicate_Caller'({})".format(
                pred_id, ", ".join(
                    ["{}" for _ in range(len(closure_exprs) - 2)]
                    + ["Env => {}, "
                       "Dbg_Img => (if Debug then new String'({})"
                       "            else null)"]
                )
            ), type=None, operands=closure_exprs)
        )

        return BuiltinCallExpr(
            "{}_Pred.Create".format(pred_id), EquationType, logic_var_exprs,
            result_var_name="Pred"
        )