def _emulate_yield_from(self, targets: Optional[List[ast.Name]], node: ast.YieldFrom) -> Iterable[ast.AST]: generator = ast.Name( id='_py_backwards_generator_{}'.format(self._name_suffix)) exception = ast.Name( id='_py_backwards_generator_exception_{}'.format(self._name_suffix)) yield ast.Assign(targets=[generator], value=ast.Call(func=ast.Name(id='iter'), args=[node.value], keywords=[])) assign_to_targets = [ ast.If(test=ast.Call(func=ast.Name(id='hasattr'), args=[ exception, ast.Str(s='value'), ], keywords=[]), body=[ ast.Assign(targets=targets, value=ast.Attribute( value=exception, attr='value')), ], orelse=[]), ast.Break()] if targets else [ast.Break()] yield ast.While(test=ast.NameConstant(value=True), body=[ ast.Try(body=[ ast.Expr(value=ast.Yield(value=ast.Call( func=ast.Name(id='next'), args=[generator], keywords=[]))), ], handlers=[ ast.ExceptHandler( type=ast.Name(id='StopIteration'), name=exception.id, body=assign_to_targets), ], orelse=[], finalbody=[]), ], orelse=[]) self._name_suffix += 1
def visit_Cast(self, node): # pylint: disable=invalid-name """Transform C cast into cast() function call.""" to_type = self.visit(node.to_type) expr = self.visit(node.expr) _ = self.visit(node.coord) return typed_ast3.Call(func=typed_ast3.Name(id='cast', ctx=typed_ast3.Load()), args=[expr], keywords=[typed_ast3.keyword(arg='type', value=to_type)])
def _set_arguments_to_super(self, call: ast.Call, first_argument: str): super_cls = ast.Name( id='cls') if first_argument == 'cls' else ast.Call( func=ast.Name(id='type'), args=[ast.Name(id=first_argument)], keywords=[]) call.args = [super_cls, ast.Name(id=first_argument)]
def _merge_dicts(self, xs: Iterable[Union[ast.Call, ast.Dict]]) \ -> ast.Call: """Creates call of function for merging dicts.""" return ast.Call( func=ast.Name(id='_py_backwards_merge_dicts'), args=[ast.List(elts=list(xs))], keywords=[])
def _Attribute(self, t): if isinstance(t.value, typed_ast3.Name) and t.value.id == 'Fortran': raise NotImplementedError( 'Fortran.{} can be handled only when subscripted.'.format( t.attr)) if isinstance(t.value, typed_ast3.Name) and t.attr == 'size': call = typed_ast3.Call(func=typed_ast3.Name(id='size', ctx=typed_ast3.Load()), args=[t.value], keywords=[]) self._Call(call) return if syntax_matches( t, typed_ast3.Attribute(value=typed_ast3.Attribute( value=typed_ast3.Name(id='st', ctx=typed_ast3.Load()), attr='generic', ctx=typed_ast3.Load()), attr='GenericVar', ctx=typed_ast3.Load())): # t._fortran_metadata = {'is_generic_var': True} # self._generic_vars.append() return self._unsupported_syntax(t) '''
def visit_Compare(self, node: ast3.Compare) -> VisitorOutput: "Transforms a comparision into a function call. E.g. `ABC == MNO` into `ABC.eq(MNO)`" assert len( node.ops ) == 1, "Pytropos only supports comparisions of two values at the time" self.generic_visit(node) op_type = type(node.ops[0]) if op_type not in compopt: raise AstTransformerError( f"Pytropos doesn't support the comparison {type(op_type)} yet, sorry :(" ) op_str = compopt[op_type] new_v = ast3.Call(func=ast3.Attribute(value=node.left, attr=op_str, ctx=ast3.Load()), args=[node.comparators[0]], keywords=[ ast3.keyword(arg='pos', value=pos_as_tuple(node), ctx=ast3.Load()) ]) return new_v
def visit_FuncCall(self, node) -> typed_ast3.Call: # pylint: disable=invalid-name """Return a call.""" name = self.visit(node.name) assert isinstance(name, typed_ast3.Name) args = self.visit(node.args) _ = self.visit(node.coord) return typed_ast3.Call(func=name, args=args, keywords=[])
def assign_target(self, node, value): if isinstance(value.tp, EmptyList): func_node = ast.Expr(value=ast.Call( func=ast.Attribute(value=node, attr="clear"), args=[])) try: self.visit(func_node) except UnknownVariable: raise StaticTypeError( "Must specify a list type when assigning an empty list") return if isinstance(node, ast.Name): left = self.context.assign_type(node.id, value.tp) self.start_line("{} = {};\n".format(left.code, value.as_value())) elif isinstance(node, ast.Subscript): container = self.get_expression_code(node.value) if isinstance(node.slice, ast.Index): index = self.get_expression_code(node.slice.value) setter = container.tp.get_method("set_item") code = setter.get_code(self.context, container, index, value) self.start_line(f"{code.code};\n") else: raise UnimplementedFeature("Slices not yet implemented") elif isinstance(node, ast.Attribute): owner = self.get_expression_code(node.value) owner.tp.set_attr(node.attr, value) left = self.get_expression_code(node) self.start_line(f"{left.code} = {value.code};\n")
def visit_JoinedStr(self, node: ast.JoinedStr) -> ast.Call: self._tree_changed = True join_call = ast.Call(func=ast.Attribute(value=ast.Str(s=''), attr='join'), args=[ast.List(elts=node.values)], keywords=[]) return self.generic_visit(join_call) # type: ignore
def make_numpy_constructor(function: str, arg: typed_ast3.AST, data_type: typed_ast3.AST) -> typed_ast3.Call: return typed_ast3.Call( func=typed_ast3.Attribute( value=typed_ast3.Name(id='np', ctx=typed_ast3.Load()), attr=function, ctx=typed_ast3.Load()), args=[arg], keywords=[typed_ast3.keyword(arg='dtype', value=data_type)])
def _prepare_lists(self, xs: List[Splitted]) -> Iterable[ListEntry]: """Wrap starred in list call and list elts to just List.""" for x in xs: if isinstance(x, ast.Starred): yield ast.Call(func=ast.Name(id='list'), args=[x.value], keywords=[]) elif x: yield ast.List(elts=x)
def _show_store_contents_expr(self) -> ast3.Expr: """Returns an ast3.Expr which prints the value of the store in the screen. Useful for debugging. """ # print(st) return ast3.Expr(value=ast3.Call( func=ast3.Name(id='print', ctx=ast3.Load()), args=[ast3.Name(id='st', ctx=ast3.Load())], keywords=[], ), )
def _prepare_splitted(self, splitted: Splitted) \ -> Iterable[Union[ast.Call, ast.Dict]]: """Wraps splitted in Call or Dict.""" for group in splitted: if not isinstance(group, list): yield ast.Call(func=ast.Name(id='dict'), args=[group], keywords=[]) elif group: yield ast.Dict(keys=[key for key, _ in group], values=[value for _, value in group])
def visit_FormattedValue(self, node: ast.FormattedValue) -> ast.Call: if node.format_spec: template = ''.join(['{:', node.format_spec.s, '}']) # type: ignore else: template = '{}' format_call = ast.Call(func=ast.Attribute(value=ast.Str(s=template), attr='format'), args=[node.value], keywords=[]) return self.generic_visit(format_call) # type: ignore
def visit_Call(self, n): args = self.visit(n.args) if n.starargs is not None: args.append(ast3.Starred(self.visit(n.starargs), ast3.Load(), lineno=n.starargs.lineno, col_offset=n.starargs.col_offset)) keywords = self.visit(n.keywords) if n.kwargs is not None: keywords.append(ast3.keyword(None, self.visit(n.kwargs))) return ast3.Call(self.visit(n.func), args, keywords)
def visit_Exec(self, n): new_globals = self.maybe_visit(n.globals) if new_globals is None: new_globals = ast3.Name("None", ast3.Load(), lineno=-1, col_offset=-1) new_locals = self.maybe_visit(n.locals) if new_locals is None: new_locals = ast3.Name("None", ast3.Load(), lineno=-1, col_offset=-1) return ast3.Expr(ast3.Call(ast3.Name("exec", ast3.Load(), lineno=n.lineno, col_offset=-1), [self.visit(n.body), new_globals, new_locals], [], lineno=n.lineno, col_offset=-1))
def visit_Print(self, n): keywords = [] if n.dest is not None: keywords.append(ast3.keyword("file", self.visit(n.dest))) if not n.nl: keywords.append(ast3.keyword("end", ast3.Str(" ", lineno=n.lineno, col_offset=-1))) return ast3.Expr(ast3.Call(ast3.Name("print", ast3.Load(), lineno=n.lineno, col_offset=-1), self.visit(n.values), keywords, lineno=n.lineno, col_offset=-1))
def visit_ListComp(self, node): from parser.functions import FunctionImplementation # calculate result type if len(node.generators) > 1: raise InvalidOperation( "Only one for statement permitted in comprehensions") comp = node.generators[0] if len(comp.ifs) > 1: raise InvalidOperation( "Only one if statement allowed in List Comprehension") assign_node = ast.Assign(targets=[comp.target], value=ast.Subscript(value=comp.iter, slice=ast.Index( ast.Num(0)))) return_node = ast.Return(value=node.elt) function_node = ast.FunctionDef(name="temp", args=ast.arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[assign_node, return_node]) function_interpreter = FunctionImplementation(function_node, (), self.context) result_type = TypeDB.get_list([function_interpreter.retval.tp]) # create temp list to hold values result = self.context.get_temp_var(result_type) self.prepends.append( f"{result.code} = {result_type.as_literal([])};\n") # create for expression append_node = ast.Expr( ast.Call(func=ast.Attribute(value=ast.Name(id=result.code, ctx=ast.Load()), attr="append", ctx=ast.Load()), args=[node.elt], keywords=[])) if comp.ifs: body = ast.If(test=comp.ifs[0], body=[append_node], orelse=[]) else: body = append_node for_node = ast.For(target=comp.target, iter=comp.iter, body=[body], orelse=[]) self.prepends.append(for_node) return result
def _Attribute(self, t): if isinstance(t.value, typed_ast3.Name) and t.value.id == 'Fortran': raise NotImplementedError( 'Fortran.{} can be handled only when subscripted.'.format( t.attr)) if isinstance(t.value, typed_ast3.Name) and t.attr == 'size': call = typed_ast3.Call(func=typed_ast3.Name(id='size', ctx=typed_ast3.Load()), args=[t.value], keywords=[]) self._Call(call) return self._unsupported_syntax(t) '''
def visit_Expr(self, node: ast3.Expr) -> VisitorOutput: """Only the internal parts of an Expr are modified, an Expr keeps being an Expr""" self.generic_visit(node) # In console mode ("single" for Python's compile) any expression statement should # print to console if self.console: return ast3.Expr(value=ast3.Call( func=ast3.Name(id='print_console', ctx=ast3.Load()), args=[node.value], keywords=[], ), ) return node
def visit_NameConstant(self, node: ast3.NameConstant) -> VisitorOutput: """Transforms name constants (None, True, False) into Pytropos values. For example, it converts:: True None into:: pt.bool(True) pt.none() """ if isinstance(node.value, bool): return ast3.Call( func=ast3.Attribute( value=ast3.Name(id='pt', ctx=ast3.Load()), attr='bool', ctx=ast3.Load(), ), args=[ast3.NameConstant(value=node.value)], keywords=[], ) elif node.value is None: return ast3.Call( func=ast3.Attribute( value=ast3.Name(id='pt', ctx=ast3.Load()), attr='none', ctx=ast3.Load(), ), args=[], keywords=[], ) else: raise AstTransformerError( f"Pytropos doesn't recognise {type(node.value)} as a constant. Sorry" )
def make_range_call(begin: t.Optional[typed_ast3.AST] = None, end: typed_ast3.AST = None, step: t.Optional[typed_ast3.AST] = None) -> typed_ast3.Call: """Create a typed_ast node equivalent to: range(begin, end, step).""" assert isinstance(end, typed_ast3.AST) if step is None: if begin is None: args = [end] else: args = [begin, end] else: assert isinstance(step, typed_ast3.AST) assert isinstance(begin, typed_ast3.AST) args = [begin, end, step] return typed_ast3.Call(func=typed_ast3.Name(id='range', ctx=typed_ast3.Load()), args=args, keywords=[])
def visit_Raise(self, n): e = None if n.type is not None: e = self.visit(n.type) if n.inst is not None and not (isinstance(n.inst, ast27.Name) and n.inst.id == "None"): inst = self.visit(n.inst) if isinstance(inst, ast3.Tuple): args = inst.elts else: args = [inst] e = ast3.Call(e, args, [], lineno=e.lineno, col_offset=-1) ret = ast3.Raise(e, None) if n.tback is not None: ret.traceback = self.visit(n.tback) return ret
def visit_Num(self, node: ast3.Num) -> VisitorOutput: """Wraps a number into a Pytropos type. Example: given the number `3` returns `pt.int(3)` """ if isinstance(node.n, (int, float)): attr = 'int' if isinstance(node.n, int) else 'float' new_v = ast3.Call(func=ast3.Attribute(value=ast3.Name( id='pt', ctx=ast3.Load()), attr=attr, ctx=ast3.Load()), args=[ast3.Num(n=node.n)], keywords=[]) return new_v else: raise AstTransformerError( f"Number of type {type(node.n)} isn't supported by pytropos. Sorry :S" )
def make_call_from_slice(slice_: typed_ast3.Slice) -> typed_ast3.Call: """Transform code like '0:n:2' into 'slice(0, n, 2)'.""" assert isinstance(slice_, typed_ast3.Slice), type(slice_) lower, upper, step = slice_.lower, slice_.upper, slice_.step if lower is None and upper is None and step is None: args = [typed_ast3.NameConstant(None)] elif lower is not None and upper is None and step is None: args = [lower, typed_ast3.NameConstant(None)] elif lower is None and upper is not None and step is None: args = [typed_ast3.NameConstant(None), upper] elif lower is not None and upper is not None and step is None: args = [lower, upper] elif lower is not None and upper is None and step is not None: args = [lower, typed_ast3.NameConstant(None), step] elif lower is not None and upper is not None and step is not None: args = [lower, upper, step] else: raise NotImplementedError('unsupported slice form: "{}"'.format(typed_ast3.dump(slice_))) return typed_ast3.Call(typed_ast3.Name('slice', typed_ast3.Load()), args, [])
def _replace_return(self, parent: Any, return_: ast.Return) -> None: """Replace return with exception raising.""" index = parent.body.index(return_) parent.body.pop(index) exception = ast.Name(id='_py_backwards_generator_return_{}'.format( self._name_suffix)) raise_exception = ast.Raise(exc=exception, cause=None) parent.body.insert(index, raise_exception) set_value = ast.Assign(targets=[ ast.Attribute(value=exception, attr='value'), ], value=return_.value) parent.body.insert(index, set_value) assign = ast.Assign(targets=[exception], value=ast.Call(func=ast.Name(id='StopIteration'), args=[], keywords=[])) parent.body.insert(index, assign)
def visit_Tuple(self, node: ast3.Tuple) -> VisitorOutput: """Transforms a tuple into a Pytropos value. For example, it converts:: (a, 5, 21) into:: pt.tuple(st['a'], pt.int(5), pt.int(21))""" self.generic_visit(node) return ast3.Call( func=ast3.Attribute( value=ast3.Name(id='pt', ctx=ast3.Load()), attr='tuple', ctx=ast3.Load(), ), args=node.elts, keywords=[], )
def visit_List(self, node: ast3.List) -> VisitorOutput: """Transforms a list into a Pytropos value. For example, it converts:: [a, 5, 21] into:: pt.list([st['a'], pt.int(5), pt.int(21)])""" self.generic_visit(node) return ast3.Call( func=ast3.Attribute( value=ast3.Name(id='pt', ctx=ast3.Load()), attr='list', ctx=ast3.Load(), ), args=[node], keywords=[], )
def visit_BinOp(self, node: ast3.BinOp) -> VisitorOutput: "Transforms a binary operation into a function call. E.g. `ABC == MNO` into `ABC.eq(MNO)`" self.generic_visit(node) op_type = type(node.op) if op_type not in operations: raise AstTransformerError( f"Pytropos doesn't support the operation {type(op_type)} yet, sorry :(" ) op_str = operations[op_type] new_v = ast3.Call(func=ast3.Attribute(value=node.left, attr=op_str, ctx=ast3.Load()), args=[node.right], keywords=[ ast3.keyword(arg='pos', value=pos_as_tuple(node), ctx=ast3.Load()) ]) return new_v
def visit_AnnAssign(self, node: ast3.AnnAssign) -> VisitorOutput: """Transforms an assignment with annotation into a pytropos type hint assignment. For example, it converts:: var: ann = expr into:: `var` = pt.type_hint(`ann`, `expr`) """ if node.value is None: raise AstTransformerError( f"{self.filename}:{node.lineno}:{node.col_offset}: Fatal Error: " "Only annotated assignments are allowed (variables with initial values). " "I.e., no full support for PEP 526 yet. Sorry :(") pos = pos_as_tuple(node) # Deleting annotation :S self.generic_visit(node) # new_node = ast3.Assign(targets=[node.target], value=node.value) return ast3.Assign( targets=[node.target], value=ast3.Call( func=ast3.Attribute( value=ast3.Name(id='pt', ctx=ast3.Load()), attr='annotation', ctx=ast3.Load(), ), args=[ node.annotation, node.value, pos if pos else ast3.Expr( value=ast3.NameConstant(value=None)) ], keywords=[], ), )