Source code for pyccel.codegen.codegen

#!/usr/bin/python
# -*- coding: utf-8 -*-

from pyccel.parser import Parser
import os

from pyccel.codegen.printing import fcode

from pyccel.ast import FunctionDef, ClassDef, Module, Program, Import, Interface
from pyccel.ast import Header, EmptyLine, NewLine, Comment
from pyccel.ast import Assign, AliasAssign, SymbolicAssign , CodeBlock
from pyccel.ast import Variable, DottedName
from pyccel.ast import For, If, While, FunctionalFor, ForIterator
from pyccel.ast import Is
from pyccel.ast import GeneratorComprehension as GC

from pyccel.parser.errors import Errors, PyccelCodegenError

# TODO improve this import

from pyccel.parser.messages import *

_extension_registry = {'fortran': 'f90'}


[docs]class Codegen(object): """Abstract class for code generator.""" def __init__(self, expr, name): """Constructor for Codegen. expr: sympy expression expression representing the AST as a sympy object name: str name of the generated module or program. """ self._ast = expr self._name = name self._kind = None self._code = None self._language = None self._stmts = {} _structs = [ 'imports', 'body', 'routines', 'classes', 'modules', 'variables', 'interfaces', ] for key in _structs: self._stmts[key] = [] self._collect_statments() self._set_kind() @property def name(self): """Returns the name associated to the source code""" return self._name @property def kind(self): """Returns the kind of the source code: Module, Program or None.""" return self._kind @property def imports(self): """Returns the imports of the source code.""" return self._stmts['imports'] @property def variables(self): """Returns the variables of the source code.""" return self._stmts['variables'] @property def body(self): """Returns the body of the source code, if it is a Program or Module.""" return self._stmts['body'] @property def routines(self): """Returns functions/subroutines.""" return self._stmts['routines'] @property def classes(self): """Returns the classes if Module.""" return self._stmts['classes'] @property def interfaces(self): """Returns the interfaces.""" return self._stmts['interfaces'] @property def modules(self): """Returns the modules if Program.""" return self._stmts['modules'] @property def is_module(self): """Returns True if a Module.""" return self.kind == 'module' @property def is_program(self): """Returns True if a Program.""" return self.kind == 'program' @property def ast(self): """Returns the AST.""" return self._ast @property def expr(self): """Returns the AST after Module/Program treatment.""" return self._expr @property def language(self): """Returns the used language""" return self._language @property def code(self): """Returns the generated code.""" return self._code def _collect_statments(self): """Collects statments and split them into routines, classes, etc.""" def collect_vars(ast): vars_ = [] for stmt in ast: if isinstance(stmt, For): if isinstance(stmt.target, Variable): vars_ += [stmt.target] + collect_vars(stmt.body) else: vars_ += stmt.target + collect_vars(stmt.body) elif isinstance(stmt, FunctionalFor): vars_ += [stmt.target] + stmt.indexes + collect_vars(stmt.loops) elif isinstance(stmt, If): vars_ += collect_vars(stmt.bodies) elif isinstance(stmt, (While, CodeBlock)): vars_ += collect_vars(stmt.body) elif isinstance(stmt, (Assign, AliasAssign)): if isinstance(stmt.lhs, Variable): if not isinstance(stmt.lhs.name, DottedName): vars_ += [stmt.lhs] return vars_ errors = Errors() errors.set_parser_stage('codegen') variables = [] routines = [] classes = [] imports = [] modules = [] body = [] interfaces = [] decs = [] for stmt in self.ast: if isinstance(stmt, EmptyLine): continue elif isinstance(stmt, FunctionDef): routines += [stmt] elif isinstance(stmt, ClassDef): classes += [stmt] elif isinstance(stmt, Import): imports += [stmt] elif isinstance(stmt, Module): modules += [stmt] elif isinstance(stmt, Interface): interfaces += [stmt] else: # TODO improve later, as in the old codegen # we don't generate code for symbolic assignments # we must also look in For While and If bodies if isinstance(stmt, SymbolicAssign): errors.report(FOUND_SYMBOLIC_ASSIGN, symbol=stmt.lhs, severity='warning') body += [Comment(str(stmt))] elif isinstance(stmt, Assign) and isinstance(stmt.rhs, Is): errors.report(FOUND_IS_IN_ASSIGN, symbol=stmt.lhs, severity='warning') body += [Comment(str(stmt))] else: body += [stmt] variables = collect_vars(self.ast) self._stmts['imports'] = imports self._stmts['variables'] = list(set(variables)) self._stmts['body'] = body self._stmts['routines'] = routines self._stmts['classes'] = classes self._stmts['modules'] = modules self._stmts['interfaces'] = interfaces errors.check() # TODO improve to have a kind = None # => must have a condition to be aprogram def _set_kind(self): """Finds the source code kind.""" # ... _stmts = (Header, EmptyLine, NewLine, Comment) body = self.body ls = [i for i in body if not isinstance(i, _stmts)] is_module = len(ls) == 0 if is_module: self._kind = 'module' else: self._kind = 'program' # ... # ... expr = None if self.is_module: expr = Module( self.name, self.variables, self.routines, self.interfaces, self.classes, imports=self.imports, ) elif self.is_program: expr = Program( self.name, self.variables, self.routines, self.interfaces, self.classes, self.body, imports=self.imports, modules=self.modules, ) else: raise NotImplementedError('TODO') self._expr = expr # ...
[docs] def doprint(self, **settings): """Prints the code in the target language.""" # ... finds the target language language = settings.pop('language', 'fortran') if not language == 'fortran': raise ValueError('Only fortran is available') self._language = language # ... # ... define the printing function to be used printer = settings.pop('printer', fcode) # ... # ... code = printer(self.expr) # ... self._code = code return code
[docs] def export(self, filename=None): ext = _extension_registry[self.language] if filename is None: filename = '{name}.{ext}'.format(name=self.name, ext=ext) else: filename = '{name}.{ext}'.format(name=filename, ext=ext) code = self.code f = open(filename, 'w') for line in code: f.write(line) f.close() return filename
[docs]class FCodegen(Codegen): pass