def visit_FunctionDef(self, node): self.curr_locals_declaration = self.passmanager.gather( LocalNodeDeclarations, node) self.current = node self.typedefs = list() self.name_to_nodes = {arg.id: {arg} for arg in node.args.args} self.yield_points = self.passmanager.gather(YieldPoints, node) # two stages, one for inter procedural propagation self.stage = 0 self.generic_visit(node) # and one for backward propagation # but this step is generally costly if cfg.getboolean('typing', 'enable_two_steps_typing'): self.stage = 1 self.generic_visit(node) # propagate type information through all aliases for name, nodes in self.name_to_nodes.items(): final_node = ast.Name("__fake__" + name, ast.Load(), None) for n in nodes: self.combine(final_node, n) for n in nodes: self.result[n] = self.result[final_node] self.current_global_declarations[node.name] = node # return type may be unset if the function always raises return_type = self.result.get(node, NamedType("pythonic::types::none_type")) self.result[node] = (Returnable(return_type), self.typedefs) for k in self.passmanager.gather(LocalNodeDeclarations, node): self.result[k] = self.get_qualifier(k)(self.result[k])
def visit_Name(self, node): if node.id in self.name_to_nodes: for n in self.name_to_nodes[node.id]: self.combine(node, n) elif node.id in self.current_global_declarations: self.combine(node, self.current_global_declarations[node.id]) else: self.result[node] = NamedType(node.id, {Weak})
def visit_Set(self, node): """ Define set type from all elements type (or empty_set type). """ self.generic_visit(node) if node.elts: for elt in node.elts: self.combine(node, elt, unary_op=SetType) else: self.result[node] = NamedType("pythonic::types::empty_set")
def visit_Assign(self, node): """ Create Assign node for final Cxx representation. It tries to handle multi assignment like: >> a = b = c = 2 If only one local variable is assigned, typing is added: >> int a = 2; TODO: Handle case of multi-assignement for some local variables. Finally, process OpenMP clause like #pragma omp atomic """ if not all(isinstance(n, (ast.Name, ast.Subscript)) for n in node.targets): raise PythranSyntaxError( "Must assign to an identifier or a subscript", node) value = self.visit(node.value) targets = [self.visit(t) for t in node.targets] alltargets = "= ".join(targets) islocal = (len(targets) == 1 and isinstance(node.targets[0], ast.Name) and node.targets[0].id in self.scope[node]) if islocal and not self.yields: # remove this decl from local decls tdecls = {t.id for t in node.targets} self.ldecls = {d for d in self.ldecls if d.id not in tdecls} # add a local declaration if self.types[node.targets[0]].iscombined(): alltargets = '{} {}'.format(self.typeof(node.targets[0]), alltargets) elif isinstance(self.types[node.targets[0]], Assignable): alltargets = '{} {}'.format( Assignable(NamedType('decltype({})'.format(value))), alltargets) else: assert isinstance(self.types[node.targets[0]], Lazy) alltargets = '{} {}'.format( Lazy(NamedType('decltype({})'.format(value))), alltargets) stmt = Assign(alltargets, value) return self.process_omp_attachements(node, stmt)
def __init__(self): self.result = dict() self.result["bool"] = NamedType("bool") self.combiners = defaultdict(UserFunction) self.current_global_declarations = dict() self.max_recompute = 1 # max number of use to be lazy ModuleAnalysis.__init__(self, StrictAliases, LazynessAnalysis) self.curr_locals_declaration = None
def visit_Num(self, node): """ Set type for number. It could be int, long or float so we use the default python to pythonic type converter. """ self.result[node] = NamedType(PYTYPE_TO_CTYPE_TABLE[type(node.n)])
def fill_constants_types(module_name, elements): """ Recursively save arguments name and default value. """ for elem, intrinsic in elements.items(): if isinstance(intrinsic, dict): # Submodule case fill_constants_types(module_name + (elem, ), intrinsic) elif isinstance(intrinsic, ConstantIntr): # use introspection to get the Python constants types cst = getattr(__import__(".".join(module_name)), elem) intrinsic.return_type = NamedType(PYTYPE_TO_CTYPE_TABLE[type(cst)])
def visit_Attribute(self, node): """ Compute typing for an attribute node. """ obj, path = attr_to_path(node) # If no type is given, use a decltype if obj.isliteral(): typename = pytype_to_ctype(obj.signature) self.result[node] = NamedType(typename) else: self.result[node] = DeclType('::'.join(path) + '{}')
def visit_Num(self, node): """ Set type for number. It could be int, long or float so we use the default python to pythonic type converter. """ ty = type(node.n) self.result[node] = NamedType(pytype_to_ctype(ty))
def visit_ExceptHandler(self, node): if node.type and node.name: if not isinstance(node.type, ast.Tuple): tname = NamedType( 'pythonic::types::{0}'.format(node.type.attr)) self.result[node.type] = tname self.combine(node.name, node.type, aliasing_type=True, register=True) list(map(self.visit, node.body))
def visit_Dict(self, node): """ Define set type from all elements type (or empty_dict type). """ self.generic_visit(node) if node.keys: for key, value in zip(node.keys, node.values): value_type = self.result[value] self.combine(node, key, unary_op=partial(DictType, of_value=value_type)) else: self.result[node] = NamedType("pythonic::types::empty_dict")
def visit_Name(self, node): if node.id in self.name_to_nodes: for n in self.name_to_nodes[node.id]: self.combine(node, n) elif node.id in self.current_global_declarations: newtype = NamedType(self.current_global_declarations[node.id].name) if node not in self.result: self.result[node] = newtype else: self.result[node] = UnknownType
def gen_for(self, node, target, local_iter, local_iter_decl, loop_body): """ Create For representation on iterator for Cxx generation. Examples -------- >> "omp parallel for" >> for i in xrange(10): >> ... do things ... Becomes >> "omp parallel for shared(__iterX)" >> for(decltype(__iterX)::iterator __targetX = __iterX.begin(); __targetX < __iterX.end(); ++__targetX) >> typename decltype(__targetX)::reference i = *__targetX; >> ... do things ... It the case of not local variable, typing for `i` disappear and typing is removed for iterator in case of yields statement in function. """ # Choose target variable for iterator (which is iterator type) local_target = "__target{0}".format(len(self.break_handlers)) local_target_decl = NamedType( "typename decltype({0})::iterator".format(local_iter)) # For yield function, all variables are globals. if self.yields: self.extra_declarations.append(( local_target, local_target_decl, )) local_target_decl = "" # If variable is local to the for body it's a ref to the iterator value # type if node.target.id in self.scope[node] and not self.yields: self.ldecls = {d for d in self.ldecls if d.id != node.target.id} local_type = "typename decltype({})::reference ".format( local_target) else: local_type = "" # Assign iterable value loop_body_prelude = Statement("{} {}= *{}".format( local_type, target, local_target)) # Create the loop loop = For( "{0} {1} = {2}.begin()".format(local_target_decl, local_target, local_iter), "{0} < {1}.end()".format(local_target, local_iter), "++{0}".format(local_target), Block([loop_body_prelude, loop_body])) return [self.process_omp_attachements(node, loop)]
def register(name, module): """ Recursively save function typing and combiners for Pythonic.""" for fname, function in module.iteritems(): if isinstance(function, dict): register(name + "::" + fname, function) else: tname = 'pythonic::{0}::functor::{1}'.format(name, fname) self.result[function] = NamedType(tname) self.combiners[function] = function if isinstance(function, Class): register(name + "::" + fname, function.fields)
def gen_for(self, node, target, local_iter, loop_body): """ Create For representation on iterator for Cxx generation. Examples -------- >> "omp parallel for" >> for i in xrange(10): >> ... do things ... Becomes >> "omp parallel for shared(__iterX)" >> for(decltype(__iterX)::iterator __targetX = __iterX.begin(); __targetX < __iterX.end(); ++__targetX) >> auto&& i = *__targetX; >> ... do things ... It the case of not local variable, typing for `i` disappear and typing is removed for iterator in case of yields statement in function. """ # Choose target variable for iterator (which is iterator type) local_target = "__target{0}".format(len(self.break_handlers)) local_target_decl = NamedType( "typename decltype({0})::iterator".format(local_iter)) # If variable is local to the for body it's a ref to the iterator value # type if node.target.id in self.scope[node] and not hasattr(self, 'yields'): self.ldecls.remove(node.target.id) local_type = "auto&&" else: local_type = "" # Assign iterable value loop_body_prelude = Statement("{} {}= *{}".format( local_type, target, local_target)) # Create the loop assign = self.make_assign(local_target_decl, local_target, local_iter) loop = For("{}.begin()".format(assign), "{0} < {1}.end()".format(local_target, local_iter), "++{0}".format(local_target), Block([loop_body_prelude, loop_body])) return [self.process_omp_attachements(node, loop)]
def visit_Str(self, node): """ Set the pythonic string type. """ self.result[node] = NamedType(pytype_to_ctype(str))
def combine_(self, node, othernode, op, unary_op, register): try: if register: # this comes from an assignment, # so we must check where the value is assigned node_id, depth = self.node_to_id(node) if depth > 0: node = ast.Name(node_id, ast.Load()) self.name_to_nodes.setdefault(node_id, set()).add(node) former_unary_op = unary_op # update the type to reflect container nesting def unary_op(x): return reduce(lambda t, n: ContainerType(t), xrange(depth), former_unary_op(x)) if isinstance(othernode, ast.FunctionDef): new_type = NamedType(othernode.name) if node not in self.result: self.result[node] = new_type else: # only perform inter procedural combination upon stage 0 if register and self.isargument(node) and self.stage == 0: node_id, _ = self.node_to_id(node) if node not in self.result: self.result[node] = unary_op(self.result[othernode]) assert self.result[node], "found an alias with a type" parametric_type = PType(self.current, self.result[othernode]) if self.register(parametric_type): current_function = self.combiners[self.current] def translator_generator(args, op, unary_op): ''' capture args for translator generation''' def interprocedural_type_translator(s, n): translated_othernode = ast.Name( '__fake__', ast.Load()) s.result[translated_othernode] = ( parametric_type.instanciate( s.current, [s.result[arg] for arg in n.args])) # look for modified argument for p, effective_arg in enumerate(n.args): formal_arg = args[p] if formal_arg.id == node_id: translated_node = effective_arg break try: s.combine(translated_node, translated_othernode, op, unary_op, register=True, aliasing_type=True) except NotImplementedError: pass # this may fail when the effective # parameter is an expression except UnboundLocalError: pass # this may fail when translated_node # is a default parameter return interprocedural_type_translator translator = translator_generator( self.current.args.args, op, unary_op) # deferred combination current_function.add_combiner(translator) else: new_type = unary_op(self.result[othernode]) if node not in self.result: self.result[node] = new_type else: self.result[node] = op(self.result[node], new_type) except UnboundableRValue: pass
MethodIntr(update_effects), "difference": ConstMethodIntr(), "difference_update": MethodIntr(update_effects), "symmetric_difference": ConstMethodIntr(), "symmetric_difference_update": MethodIntr(update_effects), "issuperset": ConstMethodIntr(return_range=prange.bool_values), "issubset": ConstMethodIntr(return_range=prange.bool_values), }, "Exception": { "args": AttributeIntr(return_type=NamedType("pythonic::types::str")), "errno": AttributeIntr(return_type=NamedType("pythonic::types::str")), "strerror": AttributeIntr(return_type=NamedType("pythonic::types::str")), "filename": AttributeIntr(return_type=NamedType("pythonic::types::str")), }, "float": { "is_integer": ConstMethodIntr(return_range=prange.bool_values), }, "complex": { "conjugate": ConstMethodIntr(), "real": AttributeIntr(return_type=NamedType("double")), "imag": AttributeIntr(return_type=NamedType("double")), }, "dict": {
def visit_Str(self, node): """ Set the pythonic string type. """ self.result[node] = NamedType(PYTYPE_TO_CTYPE_TABLE[str])