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(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 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 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()
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)) ])
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")
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" )
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)
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)
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)
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)
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" )
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" )