Beispiel #1
0
    def construct(self):
        """
        Construct a resolved expression for this map operation.

        :rtype: MapExpr
        """
        (collection_expr,
         expr,
         element_var,
         index_var,
         iter_scope) = self.construct_common()

        check_source_language(
            not self.concat or expr.type.is_collection(),
            'Cannot mapcat with expressions returning {} values (collections'
            ' expected instead)'.format(expr.type.name())
        )

        with iter_scope.use():
            filter_expr = (construct(self.filter_expr, BoolType)
                           if self.filter_expr else None)

            take_while_expr = (construct(self.take_while_expr, BoolType)
                               if self.take_while_expr else None)

        return Map.Expr(element_var, index_var, collection_expr, expr,
                        iter_scope, filter_expr, self.concat,
                        take_while_expr)
Beispiel #2
0
def env_get(env_expr, token_expr, resolve_unique=False, sequential=False):
    """
    Expression for lexical environment get operation.

    :param AbstractExpression env_expr: Expression that will yield the env
        to get the element from.
    :param AbstractExpression token_expr: Expression that will yield the
        token to use as a key on the env.
    :param bool resolve_unique: Wether we want an unique result or not.
        NOTE: For the moment, nothing will be done to ensure that only one
        result is available. The implementation will just take the first
        result.
    :param bool sequential: Whether resolution needs to be sequential or not.
    """

    sub_exprs = [construct(env_expr, LexicalEnvType),
                 construct(token_expr, Token)]

    if sequential:
        # Pass the From parameter if the user wants sequential semantics
        array_expr = 'AST_Envs.Get ({}, Get_Symbol ({}), {})'
        sub_exprs.append(construct(Self, T.root_node))
    else:
        array_expr = 'AST_Envs.Get ({}, Get_Symbol ({}))'

    make_expr = partial(BasicExpr, result_var_name="Env_Get_Result",
                        sub_exprs=sub_exprs)

    if resolve_unique:
        return make_expr("Get ({}, 0)".format(array_expr), EnvElement)
    else:
        EnvElement.array_type().add_to_context()
        return make_expr("Create ({})".format(array_expr),
                         EnvElement.array_type())
Beispiel #3
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. 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)
Beispiel #4
0
    def construct(self):
        """
        Construct a resolved expression for this.

        :rtype: EqExpr
        """
        def construct_logic_eq(lhs, rhs):
            if lhs.type == LogicVarType:
                check_source_language(
                    rhs.type == LogicVarType or rhs.type.matches(ASTNode),
                    "Operands to a logic equality operator should be either "
                    "a logic variable or an ASTNode, got {}".format(rhs.type)
                )

                # Cast the ast node type if necessary
                if (rhs.type.matches(ASTNode) and rhs.type != T.root_node):
                    rhs = Cast.Expr(rhs, T.root_node)

                return BuiltinCallExpr("Equals", EquationType, [lhs, rhs],
                                       "Equals_Pred")
            else:
                return None

        from langkit.expressions.structs import Cast
        lhs = construct(self.lhs)
        rhs = construct(self.rhs)

        # We don't care about associacivity in logic eq, so lhs and rhs
        # might be passed in reverse order.
        logic = construct_logic_eq(lhs, rhs) or construct_logic_eq(rhs, lhs)
        if logic:
            return logic

        # Don't use CompiledType.matches since in the generated code, we need
        # both operands to be *exactly* the same types, so handle specifically
        # each case.
        if issubclass(lhs.type, ASTNode):
            # Handle checks between two subclasses without explicit casts. In
            # order to help users to detect dubious checks, forbid operands
            # that can never be equal because they have no subclass in common.
            if issubclass(lhs.type, rhs.type):
                lhs = Cast.Expr(lhs, assert_type(rhs.type, ASTNode))
            elif issubclass(rhs.type, lhs.type):
                rhs = Cast.Expr(rhs, assert_type(lhs.type, ASTNode))
            else:
                check_source_language(
                    False, '{} and {} values are never equal'.format(
                        lhs.type.name().camel, rhs.type.name().camel
                    )
                )
        else:
            check_source_language(
                lhs.type == rhs.type,
                'Incompatible types for equality: {} and {}'.format(
                    lhs.type.name().camel, rhs.type.name().camel
                )
            )

        return self.make_expr(lhs, rhs)
Beispiel #5
0
    def construct(self):
        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))

        logic_var_exprs.append(
            BasicExpr("{}_Predicate_Caller'({})".format(
                pred_id, ", ".join(
                    ["{}" for _ in range(len(closure_exprs) - 1)]
                    + ["Env => {}"]
                )
            ), type=None, sub_exprs=closure_exprs)
        )

        return BuiltinCallExpr(
            "{}_Pred.Create".format(pred_id), EquationType, logic_var_exprs,
            result_var_name="Pred"
        )
Beispiel #6
0
def eval_in_env(env_expr, to_eval_expr):
    """
    Expression that will evaluate a subexpression in the context of a
    particular lexical environment. Not meant to be used directly, but instead
    via the eval_in_env shortcut.

    :param AbstractExpression env_expr: An expression that will return a
        lexical environment in which we will eval to_eval_expr.
    :param AbstractExpression to_eval_expr: The expression to eval.
    """
    return EnvBindExpr(construct(env_expr, LexicalEnvType),
                       construct(to_eval_expr))
Beispiel #7
0
def domain(logic_var_expr, domain):
    """
    Define the domain of a logical variable. Several important properties about
    this expression:

    This is the entry point into the logic DSL. A variable of LogicVarType
    *must* have a domain defined in the context of an equation. If it doesn't,
    its solution set is empty, and thus the only possible value for it is
    undefined.

    If an equation is defined in which the only constraint on variables is
    their domains, then, for a set A, B, .., N of logical variables, with
    domains DA, DB, .., DN, the set of solutions will be of the product of the
    set of every domains.

    So for example, in the equation::
        Domain(A, [1, 2]) and Domain(B, [1, 2])

    The set of solutions is::
        [(1, 1), (1, 2), (2, 1), (2, 2)]

    The 'or' operator acts like concatenation on domains of logic variable, so
    for example::

        Domain(A, [1, 2]) or Domain(A, [3, 4])

    is equivalent to (but slower than) Domain(A, [1, 2, 3, 4]).

    You can define an equation that is invalid, in that not every equation has
    a domain, and, due to runtime dispatch , we cannot statically predict if
    that's gonna happen. Thus, trying to solve such an equation will result in
    an error.

    Please note that for the moment equations can exist only on AST nodes,
    so the above examples are invalid, and just meant to illustrate the
    semantics.

    :param AbstractExpression logic_var_expr: An expression
        that must resolve to an instance of a logic variable.
    :param AbstractExpression domain: An expression that must resolve to
        the domain, which needs to be a collection of a type that
        derives from the root grammar class, or the root grammar class
        itself.
    """
    return DomainExpr(
        construct(domain, lambda d: d.is_collection(), "Type given "
                  "to LogicVar must be collection type, got {expr_type}"),
        construct(logic_var_expr, LogicVarType)
    )
Beispiel #8
0
    def construct(self):
        t = self.bind_property.struct.name()
        p = self.bind_property.name
        pred_func = untyped_literal_expr(
            "{}_{}_Logic_Binder'(Env => {})".format(
                t, p, construct(Env).render_expr()
            )
        )

        return BuiltinCallExpr(
            "{}_{}_Bind.Create".format(t, p), EquationType,
            [construct(self.from_var, LogicVarType),
             construct(self.to_var, LogicVarType),
             pred_func],
            "Bind_Result"
        )
Beispiel #9
0
def env_node(env):
    """
    Return the node associated to this environment.

    :param AbstractExpression env: The source environment.
    """
    return BasicExpr('{}.Node', T.root_node, [construct(env, LexicalEnvType)])
Beispiel #10
0
    def construct(self):
        """
        :rtype: NewExpr
        """
        # 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
        required_fields = {f._name.lower: f for f in
                           self.struct_type.get_fields()}

        error_if_not_empty(
            set(required_fields) - set(self.field_values.keys()),
            'Values are missing for {} fields'.format(
                self.struct_type.name().camel
            )
        )
        error_if_not_empty(
            set(self.field_values.keys()) - set(required_fields),
            'Extraneous fields for {}'.format(self.struct_type.name().camel)
        )

        # 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()}

        return New.Expr(self.struct_type, provided_fields)
Beispiel #11
0
 def construct(self):
     env_exprs = [construct(e, LexicalEnvType) for e in self.env_exprs]
     return BuiltinCallExpr(
         'Group', LexicalEnvType,
         [ArrayExpr(env_exprs, LexicalEnvType)],
         'Group_Env'
     )
Beispiel #12
0
    def construct(self):
        """
        Construct a resolved expression for this.

        :rtype: IfExpr
        """
        then = construct(self.then)
        else_then = construct(self.else_then)
        check_source_language(
            then.type.matches(else_then.type),
            "Mismatching types in If expression: {} and {}".format(
                then.type.name().camel, else_then.type.name().camel
            )
        )
        rtype = then.type.unify(else_then.type)
        return If.Expr(construct(self.cond, BoolType), then, else_then, rtype)
Beispiel #13
0
 def construct(self):
     return BasicExpr(
         "Variadic_{} (Relation_Array ({{}}.Items))".format(
             "Or" if self.kind == self.KIND_OR else "And"
         ),
         EquationType,
         [construct(self.equation_array, EquationType.array_type())],
         result_var_name="Logic_Boolean_Op"
     )
Beispiel #14
0
    def construct(self):
        """
        Construct a resolved expression for this.

        :rtype: EqExpr
        """
        cexpr = construct(self.expr)
        return Eq.make_expr(
            cexpr, LiteralExpr(cexpr.type.nullexpr(), cexpr.type)
        )
Beispiel #15
0
def is_visible_from(referenced_env, base_env):
    """
    Expression that will return whether an env's associated compilation unit is
    visible from another env's compilation unit.

    TODO: This is mainly exposed on envs because the CompilationUnit type is
    not exposed in the DSL yet. We might want to change that eventually if
    there are other compelling reasons to do it.

    :param AbstractExpression base_env: The environment from which we want
        to check visibility.
    :param AbstractExpression referenced_env: The environment referenced
        from base_env, for which we want to check visibility.
    """
    return BuiltinCallExpr(
        'Is_Visible_From', BoolType,
        [construct(base_env, LexicalEnvType),
         construct(referenced_env, LexicalEnvType)]
    )
Beispiel #16
0
def length(coll_expr):
    """
    Expression that will return the length of a collection.

    :param AbstractExpression coll_expr: The expression representing the
        collection to get from.
    """
    return BuiltinCallExpr(
        "Length", LongType,
        [construct(coll_expr, lambda t: t.is_collection())]
    )
Beispiel #17
0
    def construct(self):
        """
        Construct a resolved expression for this.

        :rtype: OrderingTest.Expr
        """
        return OrderingTest.Expr(self.operator, *[
            construct(e, LongType,
                      "Comparisons only work on scalars, not {expr_type}")
            for e in (self.lhs, self.rhs)
        ])
Beispiel #18
0
def get_value(logic_var):
    """
    Expression that'll extract the value out of a logic variable. The type is
    always the root grammar class.

    :param AbstractExpression logic_var: The logic var from which we want to
        extract the value.
    """
    return BuiltinCallExpr(
        "Eq_Node.Refs.GetL", T.root_node, [construct(logic_var, LogicVarType)]
    )
Beispiel #19
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"
        )
Beispiel #20
0
def env_orphan(env_expr):
    """
    Expression that will create a lexical environment copy with no parent.

    :param AbstractExpression env_expr: Expression that will return a
        lexical environment.
    """
    return BuiltinCallExpr(
        'AST_Envs.Orphan',
        LexicalEnvType,
        [construct(env_expr, LexicalEnvType)],
        'Orphan_Env'
    )
Beispiel #21
0
def collection_get(coll_expr, index_expr, or_null=True):
    """
    Expression that will get an element from a collection.

    :param AbstractExpression coll_expr: The expression representing the
        collection to get from.
    :param AbstractExpression index_expr: The expression representing the
        index of the element to get.
    :param bool or_null: If true, the expression will return null if the
        index is not valid for the collection. If False, it will raise an
        exception.
    """
    # index_expr yields a 0-based index and all the Get primitives expect
    # 0-based indexes, so there is no need to fiddle indexes here.
    index_expr = construct(index_expr, LongType)

    coll_expr = construct(coll_expr, lambda t: t.is_collection())
    or_null = construct(or_null)
    return BuiltinCallExpr(
        'Get', coll_expr.type.element_type(),
        [coll_expr, index_expr, or_null],
        'Get_Result'
    )
Beispiel #22
0
def env_group(env_array_expr):
    """
    Expression that will return a lexical environment that logically groups
    together multiple lexical environments from an array of lexical
    environments.

    :param AbstractExpression env_array_expr: Expression that will return
        an array of lexical environments. If this array is empty, the empty
        environment is returned.
    """
    return BuiltinCallExpr(
        'Group', LexicalEnvType,
        [construct(env_array_expr, LexicalEnvType.array_type())],
        'Group_Env'
    )
Beispiel #23
0
def solve(equation):
    """
    Expression that will call solve on an instance of EquationType,
    and return whether any solution was found or not. The solutions are not
    returned, instead, logic variables are bound to their values in the
    current solution.

    TODO: For the moment, since properties returning equations will
    reconstruct them everytime, there is no way to get the second solution
    if there is one. Also you cannot do that manually either since a
    property exposing equations cannot be public at the moment.

    :param AbstractExpression equation: The equation to solve.
    """
    return BuiltinCallExpr("Solve", BoolType,
                           [construct(equation, EquationType)])
Beispiel #24
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)
        astnodes = [resolve_type(a) for a in self.astnodes]
        for a in astnodes:
            check_source_language(
                issubclass(a, ASTNode),
                "Expected ASTNode subclass, 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.'
            ))
        return IsA.Expr(expr, astnodes)
Beispiel #25
0
def children(self, node):
    """
    Return `node`'s children in the AST.

    This works on both bare nodes and entities.
    """
    node_expr = construct(node)
    check_source_language(
        node_expr.type.is_ast_node or node_expr.type.is_entity_type,
        'Invalid prefix for "children": got {} but AST node or entity'
        ' expected'.format(node_expr.type.dsl_name)
    )

    if node_expr.type.is_entity_type:
        return FieldAccess.Expr(node_expr, get_builtin_field('children'),
                                [], implicit_deref=True, abstract_expr=self)
    else:
        return CallExpr('Node_Children', 'Children', T.root_node.array,
                        [node_expr], abstract_expr=self)
Beispiel #26
0
    def construct(self):
        """
        Construct a resolved expression that is the result of casting a AST
        node.

        :rtype: CastExpr
        """
        expr = construct(
            self.expr,
            lambda t: self.astnode.matches(t) or t.matches(self.astnode),
            'Cannot cast {{expr_type}} to {}: only (up/down)casting is '
            'allowed'.format(
                self.astnode.name().camel
            )
        )
        check_source_language(
            expr.type != self.astnode,
            'Casting to the same type',
            severity=Severity.warning
        )
        return Cast.Expr(expr, self.astnode, do_raise=self.do_raise)
Beispiel #27
0
    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)
Beispiel #28
0
        def __init__(self,
                     assocs_getter: PropertyDef,
                     assoc_resolver: Optional[PropertyDef],
                     transitive_parent: ResolvedExpression,
                     abstract_expr: Optional[AbstractExpression] = None):
            self.assocs_getter = assocs_getter
            self.assoc_resolver = assoc_resolver
            self.transitive_parent = transitive_parent

            assocs_getter_ref = "{}'Access".format(self.assocs_getter.name)
            assoc_resolver_ref = ('null' if self.assoc_resolver is None else
                                  "{}'Access".format(self.assoc_resolver.name))
            super().__init__(
                'Dyn_Env',
                'Create_Dynamic_Lexical_Env',
                T.LexicalEnv,
                [
                    construct(Self), assocs_getter_ref, assoc_resolver_ref,
                    transitive_parent
                ],
                abstract_expr=abstract_expr,
            )
Beispiel #29
0
def children(self, node):
    """
    Return `node`'s children in the AST.

    This works on both bare nodes and entities.
    """
    node_expr = construct(node)
    check_source_language(
        node_expr.type.is_ast_node or node_expr.type.is_entity_type,
        'Invalid prefix for "children": got {} but AST node or entity'
        ' expected'.format(node_expr.type.dsl_name))

    return build_field_access(
        node_expr,
        'children',
        [],
        lambda: CallExpr('Node_Children',
                         'Children',
                         T.root_node.array, [NullCheckExpr(node_expr)],
                         abstract_expr=self),
        abstract_expr=self,
    )
Beispiel #30
0
 def construct_op(op):
     return construct(op, lambda t: t in (BoolType, EquationType),
                      "Operands of binary logic operator must be of "
                      "boolean or equation type, got {expr_type}")
Beispiel #31
0
 def construct(self):
     return Not.make_expr(construct(self.expr, BoolType))
Beispiel #32
0
 def construct(self):
     env_exprs = [construct(e, LexicalEnvType) for e in self.env_exprs]
     return BuiltinCallExpr('Group', LexicalEnvType,
                            [ArrayExpr(env_exprs, LexicalEnvType)],
                            'Group_Env')
Beispiel #33
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"
        )
Beispiel #34
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"
        )
Beispiel #35
0
 def construct(self):
     return Not.make_expr(construct(self.expr, BoolType))
Beispiel #36
0
    def construct(self):
        """
        Constructs a resolved expression that is the result of:

        - Resolving the receiver;
        - Getting its corresponding field.

        :rtype: FieldAccessExpr
        """

        receiver_expr = construct(self.receiver)
        check_source_language(
            issubclass(receiver_expr.type, Struct),
            '{} values have no field (accessed field was {})'.format(
                receiver_expr.type.name().camel,
                self.field
            )
        )

        to_get = assert_type(
            receiver_expr.type, Struct
        ).get_abstract_fields_dict().get(self.field, None)
        ":type: AbstractNodeField"

        # If still not found, there's a problem
        check_source_language(
            to_get is not None, "Type {} has no '{}' field or property".format(
                receiver_expr.type.__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.
        check_source_language(
            len(self.arguments) == len(to_get.explicit_arguments),
            'Invalid number of arguments in the call to {}:'
            ' {} expected but got {}'.format(
                to_get.qualname,
                len(to_get.explicit_arguments),
                len(self.arguments),
            )
        )

        arg_exprs = [
            construct(
                actual, formal.type,
                custom_msg='Invalid {} actual (#{}) for {}:'.format(
                    formal.name, i, to_get.qualname,
                ) + ' expected {expected} but got {expr_type}'
            ) for i, (actual, formal) in enumerate(
                zip(self.arguments, to_get.explicit_arguments), 1
            )
        ]

        ret = FieldAccess.Expr(receiver_expr, to_get, arg_exprs)
        return ret
Beispiel #37
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 _, v, _ in self.matchers:
            scope.add(v.local_var)

        matched_expr = construct(self.matched_expr)
        check_source_language(issubclass(matched_expr.type, ASTNode),
                              'Match expressions can only work on AST nodes')

        # Yes, the assertion below is what we just checked above, but unlike
        # check_source_language, assert_type provides type information to
        # PyCharm's static analyzer.
        matched_type = assert_type(matched_expr.type, ASTNode)

        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 t, v, e in self.matchers:
            if t is not None:
                check_source_language(
                    t.matches(matched_expr.type),
                    'Cannot match {} (input type is {})'.format(
                        t.name().camel,
                        matched_expr.type.name().camel
                    )
                )
            else:
                # The default matcher (if any) matches the most general type,
                # which is the input type.
                v.set_type(matched_expr.type)
            constructed_matchers.append((construct(v), construct(e)))

        # * all possible input types must have at least one matcher. Also warn
        #   if some matchers are unreachable.
        self._check_match_coverage(matched_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 expression : "
                "{}, 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_expr,
                               match_var.type,
                               result_var=match_var)
            guard = Not.make_expr(
                Eq.make_expr(casted, LiteralExpr('null', 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 result