def visit_For(self, node: ast.For) -> None: # create list to iterate over lst_name = self.context.get_temp_name() assign_node = ast.Assign( targets=[ast.Name(id=lst_name, ctx=ast.Store())], value=node.iter) self.visit(assign_node) lst = self.context[lst_name] index = self.context.get_temp_var(TypeDB.get_type_by_name("int")) length = lst.tp.get_method("len") length_code = length.get_code(self.context, lst).code # construct for statement self.start_line( f"for({index.code}=0; {index.code} < {length_code}; {index.code}++) {{\n" ) self.indent += 4 assign_node = ast.Assign( targets=[node.target], value=ast.Subscript( value=ast.Name(id=lst_name, ctx=ast.Load()), slice=ast.Index(value=ast.Name(id=index.code, ctx=ast.Load())), ctx=ast.Load())) self.visit(assign_node) for statement in node.body: self.visit(statement) self.indent -= 4 self.start_line("}\n") self.all_paths_return = False
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_Attribute(self, node: ast3.Attribute) -> VisitorOutput: """Transforms accessing to an attribute to be handled as PythonValues do For example, it converts:: expr.val into:: expr.attr['val'] or:: expr.attr[('val', pos)]""" self.generic_visit(node) pos = pos_as_tuple(node) if pos is not None: varname = ast3.Tuple(elts=[ast3.Str(s=node.attr), pos], ctx=ast3.Load()) # type: ast3.expr else: varname = ast3.Str(s=node.attr) return ast3.Subscript( value=ast3.Attribute( value=node.value, attr='attr', ctx=ast3.Load(), ), slice=ast3.Index(value=varname, ), ctx=node.ctx, )
def visit_ArrayDecl( # pylint: disable=invalid-name self, node) -> t.Tuple[str, typed_ast3.Subscript]: """Return tuple of: name, st.ndarray[..., ...] for given array type information.""" name, type_ = self.visit(node.type) assert isinstance(name, str) assert isinstance(type_, typed_ast3.AST) dim = self.visit(node.dim) if dim is not None: raise NotImplementedError(_node_debug(node.dim), str(dim)) dim_quals = [self.visit(subnode) for subnode in node.dim_quals] if dim_quals: raise NotImplementedError(_node_debug(node.dim_quals), str(dim_quals)) _ = self.visit(node.coord) return name, typed_ast3.Subscript( value=typed_ast3.Attribute(value=typed_ast3.Name( id='st', ctx=typed_ast3.Load()), attr='ndarray', ctx=typed_ast3.Load()), slice=typed_ast3.ExtSlice(dims=[ typed_ast3.Index(value=typed_ast3.Ellipsis()), typed_ast3.Index(value=type_) # , # typed_ast3.Index(value=typed_ast3.Tuple(n=-1)) ]), ctx=typed_ast3.Load())
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 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 _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 pos_as_tuple(node: Union[ast3.expr, ast3.stmt]) -> Optional[ast3.Tuple]: if not hasattr(node, 'lineno'): return None return ast3.Tuple(elts=[ ast3.Tuple(elts=[ast3.Num(node.lineno), ast3.Num(node.col_offset)], ctx=ast3.Load()), ast3.Name(id='fn', ctx=ast3.Load()) ], ctx=ast3.Load())
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_PtrDecl(self, node): # pylint: disable=invalid-name """Return st.Pointer[...] for given pointer type.""" quals = node.quals if quals: _LOG.warning('ignoring unsupported C grammar: %s', quals) name, type_ = self.visit(node.type) assert name is None or isinstance(name, str) assert isinstance(type_, typed_ast3.AST), type(type_) _ = self.visit(node.coord) # assert type_ is not None, _node_str(node) return name, typed_ast3.Subscript( value=typed_ast3.Attribute(value=typed_ast3.Name(id='st', ctx=typed_ast3.Load()), attr='Pointer', ctx=typed_ast3.Load()), slice=typed_ast3.Index(value=type_), ctx=typed_ast3.Load())
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 visit_TypeDecl(self, node) -> t.Tuple[str, typed_ast3.Name]: # pylint: disable=invalid-name """Return a tuple: identifier and its type.""" declname = node.declname assert declname is None or isinstance(declname, str) quals = node.quals type_ = self.visit(node.type) assert isinstance(type_, typed_ast3.Name), type(type_) for qual in quals: assert isinstance(qual, str) type_ = typed_ast3.Subscript( value=typed_ast3.Attribute(value=typed_ast3.Name(id='st', ctx=typed_ast3.Load()), attr=qual.title(), ctx=typed_ast3.Load()), slice=typed_ast3.Index(value=type_), ctx=typed_ast3.Load()) _ = self.visit(node.coord) return declname, type_
def evaluate_call(node: ast3.Call, *, scope: Scope, module_path: catalog.Path) -> Node: if not is_named_tuple_definition(node, scope=scope, module_path=module_path): return node class_name_node, fields_node = node.args def field_to_parameter(field_node: ast3.expr) -> ast3.arg: name_node, annotation_node = field_node.elts return ast3.arg(ast3.literal_eval(name_node), annotation_node) initializer_node = ast3.FunctionDef( '__init__', ast3.arguments([ast3.arg('self', None)] + list(map(field_to_parameter, fields_node.elts)), None, [], [], None, []), [ast3.Pass()], [], None) function_path = evaluate_node(node.func, scope=scope, module_path=module_path) class_def = ast3.ClassDef(ast3.literal_eval(class_name_node), [ast3.Name(str(function_path), ast3.Load())], [], [initializer_node], []) return ast3.fix_missing_locations(ast3.copy_location(class_def, node))
def visit_ArrayRef(self, node): # pylint: disable=invalid-name name = self.visit(node.name) subscript = self.visit(node.subscript) _ = self.visit(node.coord) return typed_ast3.Subscript(value=name, slice=typed_ast3.Index(subscript), ctx=typed_ast3.Load())
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 visit_Struct(self, node): # pylint: disable=invalid-name """Transform Struct.""" name = node.name assert isinstance(name, str), type(name) assert node.decls is None, node.decls _ = self.visit(node.coord) return typed_ast3.Name(name, typed_ast3.Load())
def visit_IdentifierType(self, node) -> typed_ast3.Name: # pylint: disable=invalid-name """Transform IdentifierType(names: t.List[str], coord: t.Optional[Coord]).""" names = node.names assert len(names) == 1, names name = names[0] assert isinstance(name, str) _ = self.visit(node.coord) return typed_ast3.Name(id=name, ctx=typed_ast3.Load())
def _Class(self, node: ET.Element): # pylint: disable=invalid-name context = node.attrib['context'] assert context in self.namespaces, context cls_name = node.attrib['name'] if '<' in cls_name: _LOG.warning('processing template class %s', cls_name) assert '>' in cls_name cls_name, _, rest = cls_name.partition('<') rest = rest[:-1] generic_args = [_.strip() for _ in rest.split(',')] _LOG.warning('found generic args: %s', generic_args) full_name = '{}::{}'.format(self.namespaces[context], cls_name) is_stl_class = full_name in CPP_STL_CLASSES and generic_args value_type = None body = [] for member_id in node.attrib['members'].split(): if not is_stl_class: # TODO: handle non-STL classes too break if member_id not in self.all_types: continue member_type = self.all_types[member_id] if member_type.tag == 'Typedef' and member_type.attrib[ 'name'] == 'value_type': referenced_id = member_type.attrib['type'] assert referenced_id in self.all_types if referenced_id not in self.relevant_types \ and referenced_id not in self._new_relevant_types: self._new_relevant_types[referenced_id] = self.all_types[ referenced_id] _LOG.debug( 'type marked as relevant due to being container value type %s', ET.tostring( self.all_types[referenced_id]).decode().rstrip()) body.append(typed_ast3.Expr(typed_ast3.Str(referenced_id, ''))) value_type = referenced_id ''' if member_id not in self.relevant_types and member_id not in self._new_relevant_types: self._new_relevant_types[member_id] = member_type _LOG.warning('marked %s as relevant type', ET.tostring(member_type).decode().rstrip()) body.append(typed_ast3.Expr(typed_ast3.Str(member_id, ''))) ''' base_class = typed_ast3.parse(CPP_PYTHON_CLASS_PAIRS[full_name], mode='eval').body if is_stl_class: assert value_type is not None base_class = typed_ast3.Subscript( value=base_class, slice=typed_ast3.Index(typed_ast3.Str(value_type, '')), ctx=typed_ast3.Load()) return base_class
def dispatch_var_type(self, tree): code = horast.unparse(tree) stripped_code = code.strip() if stripped_code in PYTHON_FORTRAN_TYPE_PAIRS: type_name, precision = PYTHON_FORTRAN_TYPE_PAIRS[stripped_code] self.write(type_name) if precision is not None: self.write('*') self.write(str(precision)) elif _match_array(tree): sli = tree.slice assert isinstance(sli, typed_ast3.Index), typed_astunparse.dump(tree) assert isinstance(sli.value, typed_ast3.Tuple) assert len(sli.value.elts) in (2, 3), sli.value.elts elts = sli.value.elts self.dispatch_var_type(elts[1]) self.write(', ') self.write('dimension(') if len(sli.value.elts) == 2: self.write(':') else: if not self._context_input_args: self.dispatch(elts[2]) else: assert isinstance(elts[2], typed_ast3.Tuple) _LOG.warning('coercing indices of %i dims to 0-based', len(elts[2].elts)) # _LOG.warning('coercing indices of %s in %s to 0-based', arg.arg, t.name) tup_elts = [] for elt in elts[2].elts: if isinstance(elt, typed_ast3.Num): assert isinstance(elt.n, int) upper = typed_ast3.Num(n=elt.n - 1) else: assert isinstance(elt, typed_ast3.Name) upper = typed_ast3.BinOp(left=elt, op=typed_ast3.Sub(), right=typed_ast3.Num(n=1)) tup_elts.append( typed_ast3.Slice(lower=typed_ast3.Num(n=0), upper=upper, step=None)) tup = typed_ast3.Tuple(elts=tup_elts, ctx=typed_ast3.Load()) self.dispatch(tup) self.write(')') elif _match_io(tree): self.write('integer') elif isinstance(tree, typed_ast3.Call) and isinstance(tree.func, typed_ast3.Name) \ and tree.func.id == 'type': self.dispatch(tree) else: raise NotImplementedError('not yet implemented: {}'.format( typed_astunparse.dump(tree)))
def make_st_ndarray(data_type: typed_ast3.AST, dimensions_or_sizes: t.Union[int, list]) -> typed_ast3.Subscript: """Create a typed_ast node equivalent to: st.ndarray[dimensions, data_type, sizes].""" if isinstance(dimensions_or_sizes, int): dimensions = dimensions_or_sizes sizes = None else: dimensions = len(dimensions_or_sizes) sizes = [make_expression_from_slice(size) for size in dimensions_or_sizes] return typed_ast3.Subscript( value=typed_ast3.Attribute( value=typed_ast3.Name(id='st', ctx=typed_ast3.Load()), attr='ndarray', ctx=typed_ast3.Load()), slice=typed_ast3.Index(value=typed_ast3.Tuple( elts=[typed_ast3.Num(n=dimensions), data_type] + [ typed_ast3.Tuple(elts=sizes, ctx=typed_ast3.Load())] if sizes else [], ctx=typed_ast3.Load())), ctx=typed_ast3.Load())
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 _PointerType(self, node: ET.Element): # pylint: disable=invalid-name id_ = node.attrib['id'] type_ = node.attrib['type'] is_const = type_.endswith('c') if is_const: type_ = type_[:-1] try: base_type = self.fundamental_types[type_] except KeyError: # _LOG.debug() base_type = typed_ast3.Str(type_, '') type_info = typed_ast3.Subscript(value=typed_ast3.Name( id='Pointer', ctx=typed_ast3.Load()), slice=typed_ast3.Index(base_type), ctx=typed_ast3.Load()) if is_const: type_info = typed_ast3.Subscript(value=typed_ast3.Name( id='Const', ctx=typed_ast3.Load()), slice=typed_ast3.Index(type_info), ctx=typed_ast3.Load()) return (id_, type_info)
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_UnaryOp(self, node): # pylint: disable=invalid-name """Transform UnaryOp.""" op_type, op_ = C_UNARY_OPERATORS_TO_PYTHON[node.op] expr = self.visit(node.expr) _ = self.visit(node.coord) if op_type is typed_ast3.Call: return op_type(func=typed_ast3.Name(id=op_, ctx=typed_ast3.Load()), args=[expr], keywords=[]) if op_type is typed_ast3.AugAssign: return op_type(target=expr, op=op_(), value=typed_ast3.Num(n=1)) # raise NotImplementedError() return op_type(op=op_(), operand=expr)
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_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_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_Name(self, node: ast3.Name) -> VisitorOutput: """Transforms a name lookup into a dictionary lookup. For example, it converts:: var into:: st[('var', ...)] """ pos = pos_as_tuple(node) if pos is not None: varname = ast3.Tuple(elts=[ast3.Str(s=node.id), pos], ctx=ast3.Load()) # type: ast3.expr else: varname = ast3.Str(s=node.id) return ast3.Subscript(value=ast3.Name(id='st', ctx=ast3.Load()), slice=ast3.Index(value=varname), ctx=node.ctx)
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_Typedef(self, node): # pylint: disable=invalid-name """Transform Typedef.""" name = node.name assert isinstance(name, str), type(name) quals = node.quals if quals: _LOG.warning('ignoring unsupported C grammar: %s', quals) assert node.storage == ['typedef'], node.storage name_, type_ = self.visit(node.type) assert name == name_, (name, name_) _ = self.visit(node.coord) return typed_ast3.AnnAssign(target=typed_ast3.Name(name, typed_ast3.Store()), value=type_, annotation=typed_ast3.Name('type', typed_ast3.Load()), simple=1)