def get_value(self, logic_var): """ Extract the value out of a logic variable. The returned type is always the root entity type. If the variable is not defined, return a null entity. :param AbstractExpression logic_var: The logic var from which we want to extract the value. """ from langkit.expressions import If PropertyDef.get()._gets_logic_var_value = True rtype = T.root_node.entity logic_var_expr = construct(logic_var, T.LogicVarType) logic_var_ref = logic_var_expr.create_result_var('Logic_Var_Value') return If.Expr(cond=CallExpr('Is_Logic_Var_Defined', 'Eq_Node.Refs.Is_Defined', T.BoolType, [logic_var_expr]), then=CallExpr('Eq_Solution', 'Eq_Node.Refs.Get_Value', rtype, [logic_var_ref]), else_then=NullExpr(T.root_node.entity), rtype=rtype, abstract_expr=self)
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)
def collection_get(self, collection, index, or_null): """ Get the `index`\\ -th element from `collection`. Indexes are 0-based. As in Python, `index` can be negative, to retrieve elements in reverse order. For instance, ``expr.at(-1)`` will return the last element. :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 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, T.Int) coll_expr = construct(collection) as_entity = coll_expr.type.is_entity_type if as_entity: saved_coll_expr, coll_expr, entity_info = ( coll_expr.destructure_entity() ) check_source_language( coll_expr.type.is_collection, '.at prefix must be a collection: got {} instead'.format( coll_expr.type.dsl_name ) ) # We process null list nodes as empty lists, so insert a null check before # getting the collection item only if asked to raise an exception. if not or_null: if coll_expr.type.is_ast_node: coll_expr = NullCheckExpr(coll_expr) elif coll_expr.type.is_entity_type: coll_expr = NullCheckExpr(coll_expr, implicit_deref=True) coll_expr, element_type = canonicalize_list(coll_expr, to_root_list=True) or_null = construct(or_null) result = CallExpr('Get_Result', 'Get', element_type, [coll_expr, index_expr, or_null]) if as_entity: result = SequenceExpr(saved_coll_expr, make_as_entity(result, entity_info)) result.abstract_expr = self return result
def make_to_iterator( prefix: ResolvedExpression, node_data: AbstractNodeData, args: List[Optional[ResolvedExpression]], abstract_expr: Optional[AbstractExpression] = None ) -> ResolvedExpression: """ Turn an array into an iterator. :param prefix: Expression for the array to turn into an iterator. :param node_data: "to_iterator" property that this expression calls in the DSL. :param args: Arguments for the "to_iterator" property (i.e. an empty list). :param abstract_expr: See ResolvedExpression's constructor. :return: Resolved expression for the iterator creator. """ assert not args elt_type = prefix.type.element_type # Make sure we generate code for this iterator type elt_type.create_iterator(used=True) return CallExpr( result_var_name="Iter", name=node_data.name, type=elt_type.iterator, exprs=[prefix, "Self.Unit.Context"], shadow_args=[node_data], abstract_expr=abstract_expr, )
def parents_access_constructor( prefix: ResolvedExpression, node_data: AbstractNodeData, args: List[Optional[ResolvedExpression]], abstract_expr: Optional[AbstractExpression] = None ) -> ResolvedExpression: """ Return an access to the "fields" parents, whether called on a node or an entity. .. todo:: Implement rebindings shedding. """ # We expect exactly one argument: with_self. If not provided, use the # default value. assert len(args) == 1 with_self: ResolvedExpression = (args[0] or construct( node_data.natural_arguments[0].abstract_default_value)) cons_args = [with_self] return build_field_access( prefix, 'parents', cons_args, lambda: CallExpr( 'Node_Parents', 'Parents', T.root_node.array, [cast(ResolvedExpression, NullCheckExpr(prefix))] + cons_args, abstract_expr=abstract_expr, ), abstract_expr=abstract_expr, )
def construct(self): """ Construct a resolved expression for this. :rtype: IfExpr """ def construct_op(op): return construct( op, lambda t: t in (T.BoolType, T.EquationType), "Operands of binary logic operator must be of " "boolean or equation type, got {expr_type}") lhs, rhs = map(construct_op, [self.lhs, self.rhs]) check_source_language( lhs.type is rhs.type, "Left and right operands to binary logic " "operator should have the same type") if lhs.type is T.BoolType: # Boolean case if self.kind == self.AND: then = rhs else_then = LiteralExpr('False', T.BoolType) else: then = LiteralExpr('True', T.BoolType) else_then = rhs return If.Expr(lhs, then, else_then, T.BoolType) else: # Equation case kind_name = self.kind.capitalize() return CallExpr('{}_Pred'.format(kind_name), 'Logic_{}'.format(kind_name), T.EquationType, [lhs, rhs], abstract_expr=self)
def parents(self, node): """ Return an array that contains the lexical parents (this node included). Nearer parents are first in the list. This works on both bare nodes and entities. .. todo:: Implement rebindings shedding. """ node_expr = construct(node) check_source_language( node_expr.type.is_ast_node or node_expr.type.is_entity_type, 'Invalid prefix for "parents": 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('parents'), [], implicit_deref=True, abstract_expr=self) else: return CallExpr('Node_Parents', 'Parents', T.root_node.array, [node_expr], abstract_expr=self)
def construct(self): separator = construct(self.separator, T.String) strings = construct(self.strings, T.String.array) return CallExpr("Join_Result", "Join_Strings", T.String, [separator, strings], abstract_expr=self)
def unique(self, array): """ Return a copy of `array` with duplicated elements removed. """ from langkit.compile_context import ADA_BODY array_expr = construct(array) array_type = array_expr.type check_source_language( array_type.is_array_type, 'Array expected but got {} instead'.format(array_type.dsl_name)) element_type = array_type.element_type check_source_language( element_type.hashable, 'Element type (here {}) must be hashable'.format( element_type.dsl_name)) # Enable the generation of the function that does the actual work get_context().add_with_clause('Implementation', ADA_BODY, 'Ada.Containers.Hashed_Sets') array_type.require_unique_function() return CallExpr('Unique_Array', 'Make_Unique', array_type, [array_expr], abstract_expr=self)
def construct(self): env_exprs = [construct(e, T.LexicalEnvType) for e in self.env_exprs] array_arg = LiteralExpr(array_aggr(['{}' for _ in env_exprs]), no_compiled_type, env_exprs) return CallExpr('Group_Env', 'Group', T.LexicalEnvType, [array_arg], abstract_expr=self)
def rebind_env(self, env, rebindings): """ Return a new environment based on `env` to include the given `rebindings`. """ return CallExpr('Rebound_Env', 'Rebind_Env', T.LexicalEnv, [construct(env, T.LexicalEnv), construct(rebindings, T.EnvRebindings)], abstract_expr=self)
def env_node(self, env): """ Return the node associated to the `env` environment. :param AbstractExpression env: The source environment. """ return CallExpr('Env_Node', 'AST_Envs.Env_Node', T.root_node, [construct(env, T.LexicalEnv)], abstract_expr=self)
def length(self, collection): """ Compute the length of `collection`. """ return CallExpr('Len', 'Length', T.LongType, [construct(collection, lambda t: t.is_collection)], abstract_expr=self)
def make_expr(cls, lhs, rhs, abstract_expr=None): if lhs.type.is_entity_type: return cls.make_expr_for_entities(lhs, rhs, abstract_expr) elif lhs.type.has_equivalent_function: return CallExpr('Is_Equal', 'Equivalent', T.Bool, [lhs, rhs], abstract_expr=abstract_expr) else: return BasicExpr('Is_Equal', '{} = {}', T.Bool, [lhs, rhs], abstract_expr=abstract_expr)
def make_expr_for_entities(lhs, rhs, abstract_expr=None): from langkit.expressions.structs import Cast if lhs.type != T.entity: lhs = Cast.Expr(lhs, T.entity) if rhs.type != T.entity: rhs = Cast.Expr(rhs, T.entity) return CallExpr('Is_Equiv', 'Equivalent', T.Bool, [lhs, rhs], abstract_expr=abstract_expr)
def concat_rebindings(self, lhs, rhs): """ Combine rebindings from the `lhs` and `rhs` environment rebindings. """ return CallExpr('Rebinding', 'AST_Envs.Combine', T.EnvRebindings, [construct(lhs, T.EnvRebindings), construct(rhs, T.EnvRebindings)], abstract_expr=self)
def env_orphan(self, env): """ Return a copy of the `env` lexical environment which has no parent. :param AbstractExpression env: Expression that will return a lexical environment. """ return CallExpr('Orphan_Env', 'AST_Envs.Orphan', T.LexicalEnv, [construct(env, T.LexicalEnv)], abstract_expr=self)
def construct(self): # The equation constructor takes an Ada array as a paramater, not our # access to record: unwrap it. relation_array = untyped_literal_expr( 'Relation_Array ({}.Items)', [construct(self.equation_array, T.EquationType.array)]) return CallExpr('Logic_Boolean_Op', 'Logic_{}'.format(self.kind_name), T.EquationType, [relation_array], abstract_expr=self)
def env_parent(self, env): """ Return the parent of the `env` lexical environment. :param AbstractExpression env: The source environment. """ return CallExpr( 'Env_Parent', 'AST_Envs.Parent', T.LexicalEnv, [construct(env, T.LexicalEnv)], abstract_expr=self, )
def construct(self): # The equation constructor takes an Ada array as a parameter, not our # access to record: unwrap it. relation_array = untyped_literal_expr( 'Relation_Array ({}.Items)', [construct(self.equation_array, T.Equation.array)]) return CallExpr( "Logic_Boolean_Op", f"Solver.Create_{self.kind_name}", T.Equation, [relation_array, sloc_info_arg(self.location)], abstract_expr=self)
def collection_get(self, collection, index, or_null): """ Get the `index`\ -th element from `collection`. Indexes are 0-based. As in Python, `index` can be negative, to retrieve elements in reverse order. For instance, ``expr.at(-1)`` will return the last element. :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 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, T.LongType) coll_expr = construct(collection) as_entity = coll_expr.type.is_entity_type if as_entity: saved_coll_expr, coll_expr, entity_info = ( coll_expr.destructure_entity()) check_source_language( coll_expr.type.is_collection, '.at prefix must be a collection: got {} instead'.format( coll_expr.type.dsl_name)) or_null = construct(or_null) result = CallExpr('Get_Result', 'Get', coll_expr.type.element_type, [coll_expr, index_expr, or_null]) if as_entity: result = SequenceExpr(saved_coll_expr, make_as_entity(result, entity_info)) result.abstract_expr = self return result
def length(self, collection): """ Compute the length of `collection`. """ coll_expr = construct(collection) orig_type = coll_expr.type if coll_expr.type.is_entity_type: coll_expr = FieldAccessExpr(coll_expr, 'Node', coll_expr.type.astnode, do_explicit_incref=False) check_source_language( coll_expr.type.is_collection, 'Collection expected but got {} instead'.format(orig_type.dsl_name)) return CallExpr('Len', 'Length', T.Int, [coll_expr], abstract_expr=self)
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( self, node_expr, 'children', lambda node_expr, abstract_expr: CallExpr( 'Node_Children', 'Children', T.root_node.array, [node_expr], abstract_expr=self))
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)
def length(self: AbstractExpression, collection: AbstractExpression) -> ResolvedExpression: """ Compute the length of `collection`. """ coll_expr = construct(collection) orig_type = coll_expr.type # Automatically unwrap entities if coll_expr.type.is_entity_type: coll_expr = FieldAccessExpr(coll_expr, 'Node', coll_expr.type.astnode, do_explicit_incref=False) check_source_language( coll_expr.type.is_collection, 'Collection expected but got {} instead'.format(orig_type.dsl_name)) coll_expr, _ = canonicalize_list(coll_expr) return CallExpr('Len', 'Length', T.Int, [coll_expr], abstract_expr=self)
def solve(self, equation): """ Call ``solve`` on the given `equation` 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. """ PropertyDef.get()._solves_equation = True return CallExpr('Solve_Success', 'Solve_Wrapper', T.BoolType, [construct(equation, T.EquationType), construct(Self, T.root_node)], abstract_expr=self)
def env_group(self, env_array, with_md=None): """ Return a new lexical environment that logically groups together multiple environments. `env_array` must be an array that contains the environments to be grouped. If it is empty, the empty environment is returned. If provided, `with_md` must be a metadata structure: it will be made the default metadata for this lexical environment. :type env_array: AbstractExpression :type with_md: AbstractExpression """ from langkit.expressions import No if not with_md: with_md = No(T.env_md) return CallExpr('Group_Env', 'Group', T.LexicalEnv, [construct(env_array, T.LexicalEnv.array), construct(with_md, T.env_md)], abstract_expr=self)
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 CallExpr('Concat_Result', 'Concat', array_1.type, [array_1, array_2], abstract_expr=self)
def construct(self): """ Construct a resolved expression for this. :rtype: OrderingTest.Expr """ lhs, rhs = construct(self.lhs), construct(self.rhs) check_source_language( lhs.type.is_long_type or lhs.type.is_big_integer_type or lhs.type.is_ast_node, 'Comparisons only work on {}, {} or nodes not {}' .format(T.Int.dsl_name, T.BigInt.dsl_name, lhs.type.dsl_name) ) # If we are comparing two nodes, just use the dedicated helper if lhs.type.is_ast_node: check_source_language( rhs.type.is_ast_node, 'A node can only be compared to another node (got {} and {})' .format(lhs.type.dsl_name, rhs.type.dsl_name) ) relation = {self.LT: 'Less_Than', self.LE: 'Less_Or_Equal', self.GT: 'Greater_Than', self.GE: 'Greater_Or_Equal'}[self.operator] return CallExpr('Node_Comp', 'Compare', T.Bool, [lhs, rhs, relation], abstract_expr=self) # Otherwise, expect strict equality for both operands and use the # native comparison operator for code generation. check_source_language( lhs.type == rhs.type, 'Comparisons require the same type for both operands' ' (got {} and {})'.format(lhs.type.dsl_name, rhs.type.dsl_name) ) return OrderingTest.Expr(self.operator, lhs, rhs)
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)