Source code for pyccel.parser.parser

# -*- coding: utf-8 -*-

from redbaron import RedBaron
from redbaron import StringNode, IntNode, FloatNode, ComplexNode
from redbaron import FloatExponantNode, StarNode
from redbaron import NameNode
from redbaron import AssignmentNode
from redbaron import CommentNode, EndlNode
from redbaron import ComparisonNode
from redbaron import ComparisonOperatorNode
from redbaron import UnitaryOperatorNode
from redbaron import BinaryOperatorNode, BooleanOperatorNode
from redbaron import AssociativeParenthesisNode
from redbaron import DefNode
from redbaron import ClassNode
from redbaron import TupleNode, ListNode
from redbaron import CommaProxyList
from redbaron import LineProxyList
from redbaron import ListComprehensionNode
from redbaron import ComprehensionLoopNode
from redbaron import ArgumentGeneratorComprehensionNode
from redbaron import NodeList
from redbaron import DotProxyList
from redbaron import ReturnNode
from redbaron import PassNode
from redbaron import DefArgumentNode
from redbaron import ForNode
from redbaron import PrintNode
from redbaron import DelNode
from redbaron import DictNode, DictitemNode
from redbaron import WhileNode
from redbaron import IfelseblockNode, IfNode, ElseNode, ElifNode
from redbaron import TernaryOperatorNode
from redbaron import DotNode
from redbaron import CallNode
from redbaron import CallArgumentNode
from redbaron import AssertNode
from redbaron import ExceptNode
from redbaron import FinallyNode
from redbaron import RaiseNode
from redbaron import TryNode
from redbaron import YieldNode
from redbaron import YieldAtomNode
from redbaron import BreakNode, ContinueNode
from redbaron import GetitemNode, SliceNode
from redbaron import ImportNode, FromImportNode
from redbaron import DottedAsNameNode, DecoratorNode
from redbaron import NameAsNameNode
from redbaron import LambdaNode
from redbaron import WithNode
from redbaron import AtomtrailersNode

from pyccel.ast import NativeInteger, NativeReal
from pyccel.ast import NativeBool, NativeComplex
from pyccel.ast import NativeRange
from pyccel.ast import NativeIntegerList
from pyccel.ast import NativeRealList
from pyccel.ast import NativeComplexList
from pyccel.ast import NativeList
from pyccel.ast import NativeSymbol
from pyccel.ast import String
from pyccel.ast import DataTypeFactory
from pyccel.ast import Nil, Void
from pyccel.ast import Variable
from pyccel.ast import DottedName, DottedVariable
from pyccel.ast import Assign, AliasAssign, SymbolicAssign
from pyccel.ast import AugAssign,CodeBlock
from pyccel.ast import Return
from pyccel.ast import Pass
from pyccel.ast import ConstructorCall
from pyccel.ast import FunctionDef, Interface
from pyccel.ast import PythonFunction,SympyFunction
from pyccel.ast import ClassDef
from pyccel.ast import GetDefaultFunctionArg
from pyccel.ast import For, FunctionalFor, ForIterator
from pyccel.ast import GeneratorComprehension as GC
from pyccel.ast import FunctionalSum, FunctionalMax, FunctionalMin
from pyccel.ast import If, IfTernaryOperator
from pyccel.ast import While
from pyccel.ast import Print
from pyccel.ast import SymbolicPrint
from pyccel.ast import Del
from pyccel.ast import Assert
from pyccel.ast import Comment, EmptyLine, NewLine
from pyccel.ast import Break, Continue
from pyccel.ast import Slice, IndexedVariable, IndexedElement
from pyccel.ast import FunctionHeader, ClassHeader, MethodHeader
from pyccel.ast import VariableHeader, InterfaceHeader
from pyccel.ast import MetaVariable
from pyccel.ast import MacroFunction, MacroVariable
from pyccel.ast import Concatinate
from pyccel.ast import ValuedVariable
from pyccel.ast import Argument, ValuedArgument
from pyccel.ast import Is
from pyccel.ast import Import, TupleImport
from pyccel.ast import AsName
from pyccel.ast import AnnotatedComment, CommentBlock
from pyccel.ast import With, Block
from pyccel.ast import Range, Zip, Enumerate, Product, Map
from pyccel.ast import List, Dlist, Len
from pyccel.ast import builtin_function as pyccel_builtin_function
from pyccel.ast import builtin_import as pyccel_builtin_import
from pyccel.ast import builtin_import_registery as pyccel_builtin_import_registery
from pyccel.ast import Macro
from pyccel.ast import MacroShape
from pyccel.ast import construct_macro
from pyccel.ast import SumFunction, Subroutine
from pyccel.ast import Zeros
from pyccel.ast import inline, subs
from pyccel.ast.datatypes import sp_dtype, str_dtype
from pyccel.ast.core import local_sympify, int2float, Pow, _atomic
from sympy import Pow as sp_Pow

from pyccel.parser.utilities import omp_statement, acc_statement
from pyccel.parser.utilities import fst_move_directives
from pyccel.parser.utilities import reconstruct_pragma_multilines
from pyccel.parser.utilities import is_valid_filename_pyh, is_valid_filename_py
from pyccel.parser.utilities import read_file
from pyccel.parser.utilities import get_default_path

from pyccel.parser.syntax.headers import parse as hdr_parse
from pyccel.parser.syntax.openmp import parse as omp_parse
from pyccel.parser.syntax.openacc import parse as acc_parse

from pyccel.parser.errors import Errors, PyccelSyntaxError
from pyccel.parser.errors import PyccelSemanticError

# TODO - remove import * and only import what we need
#      - use OrderedDict whenever it is possible

from pyccel.parser.messages import *

from sympy import Symbol, sympify,symbols
from sympy import Tuple
from sympy import NumberSymbol, Number
from sympy import Integer, Float
from sympy import Add, Mul, floor, Mod
from sympy import FunctionClass
from sympy import Lambda
from sympy import ceiling
from sympy import Atom
from sympy import cse

from sympy.core.expr import Expr
from sympy.core.relational import Eq, Ne, Lt, Le, Gt, Ge
from sympy.core.containers import Dict
from sympy.core.function import Function, FunctionClass, Application
from sympy.core.numbers import ImaginaryUnit
from sympy.logic.boolalg import And, Or
from sympy.logic.boolalg import true, false
from sympy.logic.boolalg import Not
from sympy.logic.boolalg import Boolean, BooleanTrue, BooleanFalse
from sympy.tensor import Indexed, IndexedBase
from sympy.utilities.iterables import iterable as sympy_iterable
from sympy.core.assumptions import StdFactKB
from sympy import Sum as Summation, Heaviside, KroneckerDelta, Min, Max
from sympy import oo as INF
from sympy import cache
from collections import OrderedDict

import traceback
import importlib
import pickle
import os
import sys
import re
strip_ansi_escape = \
    re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]|[\n\t\r]')
import redbaron
redbaron.ipython_behavior = False

# use this to delete ansi_escape characters from a string
# Useful for very coarse version differentiation.

PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3


#  ... useful functions for imports
# TODO installed modules. must ask python (working version) where the module is
#      installed

[docs]def is_ignored_module(name): if isinstance(name, DottedName): if str(name) in ['pyccel.decorators']: return True return False
[docs]def get_filename_from_import(module,output_folder='',context_import_path = {}): """Returns a valid filename with absolute path, that corresponds to the definition of module. The priority order is: - header files (extension == pyh) - python files (extension == py) """ filename_pyh = '{}.pyh'.format(module.replace('.','/')) filename_py = '{}.py'.format(module.replace('.','/')) if is_valid_filename_pyh(filename_pyh): return os.path.abspath(filename_pyh) if is_valid_filename_py(filename_py): return os.path.abspath(filename_py) if (module in context_import_path): poss_filename_pyh = '{0}{1}.pyh'.format(context_import_path[module],module) poss_filename_py = '{0}{1}.py'.format(context_import_path[module],module) if is_valid_filename_pyh(poss_filename_pyh): return os.path.abspath(poss_filename_pyh) if is_valid_filename_py(poss_filename_py): return os.path.abspath(poss_filename_py) folders = output_folder.split(""".""") for i in range(len(folders)): poss_dirname = os.path.join( *folders[:i+1] ) poss_filename_pyh = os.path.join( poss_dirname, filename_pyh ) poss_filename_py = os.path.join( poss_dirname, filename_py ) if is_valid_filename_pyh(poss_filename_pyh): return os.path.abspath(poss_filename_pyh) if is_valid_filename_py(poss_filename_py): return os.path.abspath(poss_filename_py) source = module if len(module.split(""".""")) > 1: # we remove the last entry, since it can be a pyh file source = """.""".join(i for i in module.split(""".""")[:-1]) _module = module.split(""".""")[-1] filename_pyh = '{}.pyh'.format(_module) filename_py = '{}.py'.format(_module) try: package = importlib.import_module(source) package_dir = str(package.__path__[0]) except: errors = Errors() errors.report(PYCCEL_UNFOUND_IMPORTED_MODULE, symbol=source, severity='fatal') filename_pyh = os.path.join(package_dir, filename_pyh) filename_py = os.path.join(package_dir, filename_py) if os.path.isfile(filename_pyh): return filename_pyh elif os.path.isfile(filename_py): return filename_py errors = Errors() errors.report(PYCCEL_UNFOUND_IMPORTED_MODULE, symbol=module, severity='fatal')
# ... # ... def _get_name(var): """.""" if isinstance(var, (Symbol, IndexedVariable, IndexedBase)): return str(var) if isinstance(var, (IndexedElement, Indexed)): return str(var.base) if isinstance(var, Application): return str(type(var).__name__) raise NotImplementedError('Uncovered type {dtype}'.format(dtype=type(var)))
[docs]class Parser(object): """ Class for a Parser.""" def __init__(self, inputs, debug=False, headers=None, static=None, show_traceback=True, output_folder='', context_import_path = {}): """Parser constructor. inputs: str filename or code to parse as a string debug: bool True if in debug mode. headers: list, tuple list of headers to append to the namespace static: list/tuple a list of 'static' functions as strings show_traceback: bool prints Tracebacke exception if True """ self._fst = None self._ast = None self._filename = None self._metavars = {} self._namespace = {} self._namespace['imports'] = OrderedDict() self._namespace['variables'] = {} self._namespace['classes'] = {} self._namespace['functions'] = {} self._namespace['macros'] = {} self._namespace['cls_constructs'] = {} self._namespace['symbolic_functions'] = {} self._namespace['python_functions'] = {} self._scope = {} self._output_folder = output_folder self._context_import_path = context_import_path # represent the namespace of a function self._current_class = None self._current = None # we use it to detect the current method or function self._imports = {} # we use it to store the imports self._parents = [] # a Parser can have parents, who are importing it. # imports are then its sons. self._sons = [] self._d_parsers = OrderedDict() # the following flags give us a status on the parsing stage self._syntax_done = False self._semantic_done = False # current position for errors self._bounding_box = None # flag for blocking errors. if True, an error with this flag will cause # Pyccel to stop # TODO ERROR must be passed to the Parser __init__ as argument self._blocking = False # printing exception self._show_traceback = show_traceback # TODO use another name for headers # => reserved keyword, or use __ self._namespace['headers'] = {} if headers: if not isinstance(headers, dict): raise TypeError('Expecting a dict of headers') for (key, value) in list(headers.items()): self._namespace['headers'][key] = value self._namespace['static'] = [] if static: if not isinstance(static, (list, tuple)): raise TypeError('Expecting a list/tuple of static') for i in static: if not isinstance(i, str): raise TypeError('Expecting str. given {}'.format(type(i))) self._namespace['static'] = static # check if inputs is a file code = inputs if os.path.isfile(inputs): # we don't use is_valid_filename_py since it uses absolute path # file extension ext = inputs.split(""".""")[-1] if not ext in ['py', 'pyh']: errors = Errors() errors.report(INVALID_FILE_EXTENSION, symbol=ext, severity='fatal') errors.check() raise SystemExit(0) code = read_file(inputs) self._filename = inputs self._code = code try: red = RedBaron(code) except Exception as e: errors = Errors() errors.report(INVALID_PYTHON_SYNTAX, symbol='\n' + str(e), severity='fatal') errors.check() raise SystemExit(0) red = fst_move_directives(red) self._fst = red @property def namespace(self): return self._namespace @property def headers(self): return self.namespace['headers'] @property def imports(self): return self.namespace['imports'] @property def functions(self): return self.namespace['functions'] @property def variables(self): return self.namespace['variables'] @property def classes(self): return self.namespace['classes'] @property def python_functions(self): return self.namespace['python_functions'] @property def symbolic_functions(self): return self.namespace['symbolic_functions'] @property def static_functions(self): return self.namespace['static'] @property def macros(self): return self.namespace['macros'] @property def filename(self): return self._filename @property def code(self): return self._code @property def fst(self): return self._fst @property def ast(self): if self._ast is None: self._ast = self.parse() return self._ast @property def metavars(self): return self._metavars @property def current_class(self): return self._current_class @property def syntax_done(self): return self._syntax_done @property def semantic_done(self): return self._semantic_done @property def parents(self): """Returns the parents parser.""" return self._parents @property def sons(self): """Returns the sons parser.""" return self._sons @property def d_parsers(self): """Returns the d_parsers parser.""" return self._d_parsers @property def is_header_file(self): """Returns True if we are treating a header file.""" if self.filename: return self.filename.split(""".""")[-1] == 'pyh' else: return False @property def bounding_box(self): return self._bounding_box @property def blocking(self): return self._blocking @property def show_traceback(self): return self._show_traceback # TODO shall we need to export the Parser too?
[docs] def dump(self, filename=None): """Dump the current ast using Pickle. filename: str output file name. if not given `name.pyccel` will be used and placed in the Pyccel directory ($HOME/.pyccel) """ # ... use_home_dir = False if not filename: if not self.filename: raise ValueError('Expecting a filename to load the ast') use_home_dir = True name = os.path.basename(self.filename) filename = '{}.pyccel'.format(name) # check extension if not filename.split(""".""")[-1] == 'pyccel': raise ValueError('Expecting a .pyccel extension') # print('>>> home = ', os.environ['HOME']) # ... # we are only exporting the AST. f = open(filename, 'wb') pickle.dump(self.ast, f, protocol=2) f.close() print ('> exported :', self.ast)
# TODO shall we need to load the Parser too?
[docs] def load(self, filename=None): """Load the current ast using Pickle. filename: str output file name. if not given `name.pyccel` will be used and placed in the Pyccel directory ($HOME/.pyccel) """ # ... use_home_dir = False if not filename: if not self.filename: raise ValueError('Expecting a filename to load the ast') use_home_dir = True name = os.path.basename(self.filename) filename = '{}.pyccel'.format(name) # check extension if not filename.split(""".""")[-1] == 'pyccel': raise ValueError('Expecting a .pyccel extension') # print('>>> home = ', os.environ['HOME']) # ... f = open(filename, 'rb') self._ast = pickle.load(f) f.close() print ('> loaded :', self.ast)
[docs] def append_parent(self, parent): """.""" # TODO check parent is not in parents self._parents.append(parent)
[docs] def append_son(self, son): """.""" # TODO check son is not in sons self._sons.append(son)
[docs] def parse(self, d_parsers=None, verbose=False): """converts redbaron fst to sympy ast.""" if self.syntax_done: print ('> syntax analysis already done') return self.ast # TODO - add settings to Errors # - filename errors = Errors() if self.filename: errors.set_target(self.filename, 'file') errors.set_parser_stage('syntax') # we add the try/except to allow the parser to find all possible errors try: ast = self._fst_to_ast(self.fst) except Exception as e: errors.check() if self.show_traceback: traceback.print_exc() raise SystemExit(0) self._ast = ast errors.check() self._syntax_done = True if d_parsers is None: d_parsers = OrderedDict() self._d_parsers = self._parse_sons(d_parsers, verbose=verbose) return ast
[docs] def annotate(self, **settings): """.""" if self.semantic_done: print ('> semantic analysis already done') return self.ast # TODO - add settings to Errors # - filename errors = Errors() if self.filename: errors.set_target(self.filename, 'file') errors.set_parser_stage('semantic') # we first treat all sons to get imports verbose = settings.pop('verbose', False) self._annotate_parents(verbose=verbose) # then we treat the current file ast = self.ast # we add the try/except to allow the parser to find all possible errors try: ast = self._annotate(ast, **settings) except Exception as e: errors.check() if self.show_traceback: traceback.print_exc() raise SystemExit(0) self._ast = ast # in the case of a header file, we need to convert all headers to # FunctionDef etc ... if self.is_header_file: target = [] for parent in self.parents: for (key, item) in parent.imports.items(): if get_filename_from_import(key) == self.filename: target += item target = set(target) target_headers = target.intersection(self.headers.keys()) for name in list(target_headers): v = self.headers[name] if isinstance(v, FunctionHeader) and not isinstance(v, MethodHeader): F = self.get_function(name) if F is None: interfaces = v.create_definition() for F in interfaces: self.insert_function(F) else: errors.report(IMPORTING_EXISTING_IDENTIFIED, symbol=name, blocker=True, severity='fatal') errors.check() self._semantic_done = True return ast
def _parse_sons(self, d_parsers, verbose=False): """Recursive algorithm for syntax analysis on a given file and its dependencies. This function always terminates with an OrderedDict that contains parsers for all involved files. """ treated = set(d_parsers.keys()) imports = set(self.imports.keys()) imports = imports.difference(treated) if not imports: return d_parsers for source in imports: if verbose: print ('>>> treating :: {}'.format(source)) # get the absolute path corresponding to source filename = get_filename_from_import(source,self._output_folder,self._context_import_path) q = Parser(filename) q.parse(d_parsers=d_parsers) d_parsers[source] = q # link self to its sons imports = list(self.imports.keys()) for source in imports: d_parsers[source].append_parent(self) self.append_son(d_parsers[source]) return d_parsers def _annotate_parents(self, **settings): verbose = settings.pop('verbose', False) # ... def _update_from_son(p): # TODO - only import what is needed # - use insert_variable etc for entry in ['variables', 'classes', 'functions', 'cls_constructs']: d_self = self._namespace[entry] d_son = p.namespace[entry] for (k, v) in list(d_son.items()): d_self[k] = v # ... # we first treat sons that have no imports for p in self.sons: if not p.sons: if verbose: print ('>>> treating :: {}'.format(p.filename)) p.annotate(**settings) # finally we treat the remaining sons recursively for p in self.sons: if p.sons: if verbose: print ('>>> treating :: {}'.format(p.filename)) p.annotate(**settings)
[docs] def print_namespace(self): # TODO improve spacing print ('------- namespace -------') for (k, v) in self.namespace.items(): print ('{var} \t :: \t {dtype}'.format(var=k, dtype=type(v))) print ('-------------------------')
[docs] def view_namespace(self, entry): # TODO improve try: from tabulate import tabulate table = [] for (k, v) in self.namespace[entry].items(): k_str = '{}'.format(k) if entry == 'imports': if v is None: v_str = '*' else: v_str = '{}'.format(v) elif entry == 'variables': v_str = '{}'.format(type(v)) else: raise NotImplementedError('TODO') line = [k_str, v_str] table.append(line) headers = ['module', 'target'] # txt = tabulate(table, headers, tablefmt="rst") txt = tabulate(table, tablefmt='rst') print (txt) except: print ('------- namespace.{} -------'.format(entry)) for (k, v) in self.namespace[entry].items(): print ('{var} \t :: \t {value}'.format(var=k, value=v)) print ('-------------------------')
[docs] def dot(self, filename): """Exports sympy AST using graphviz then convert it to an image.""" expr = self.ast graph_str = dotprint(expr) f = open(filename, 'w') f.write(graph_str) f.close() # name without path name = os.path.basename(filename) # name without extension name = os.path.splitext(name)[0] cmd = 'dot -Tps {name}.gv -o {name}.ps'.format(name=name) os.system(cmd)
[docs] def insert_import(self, expr): """.""" # TODO improve if not isinstance(expr, Import): raise TypeError('Expecting Import expression') container = self._namespace['imports'] if self._current: self._scope[self._current]['imports'].append(expr) # if source is not specified, imported things are treated as sources # TODO test if builtin import source = expr.source if source is None: for t in expr.target: name = str(t) container[name] = None else: source = str(source) if not source in pyccel_builtin_import_registery: for t in expr.target: name = [str(t)] if not source in container.keys(): container[source] = [] container[source] += name
[docs] def get_variable(self, name): """.""" if self.current_class: for i in self._current_class.attributes: if str(i.name) == name: var = i return var if self._current: if name in self._scope[self._current]['variables']: return self._scope[self._current]['variables'][name] if name in self._imports[self._current]: return self._imports[self._current][name] if isinstance(self._current, DottedName): scp = self._current.name[0] if name in self._scope[scp]['variables']: return self._scope[scp]['variables'][name] if name in self._imports[scp]: return self._imports[scp][name] if name in self._namespace['variables']: return self._namespace['variables'][name] if name in self._imports: return self._imports[name] return None
[docs] def get_variables(self, source=None): if source == 'parent': if isinstance(self._current, DottedName): name = self._current.name[0] elif not self._current is None: return self._namespace['variables'].values() else: raise TypeError('there is no parent to extract variables from ' ) return self._scope[name]['variables'].values() else: return self._scope[self._current]['variables'].values()
[docs] def insert_variable(self, expr, name=None): """.""" # TODO add some checks before if name is None: name = str(expr) if not isinstance(expr, Variable): raise TypeError('variable must be of type Variable') if self._current: self._scope[self._current]['variables'][name] = expr else: self._namespace['variables'][name] = expr
[docs] def create_variable(self, expr, store=False): """.""" import numpy as np try: name = 'result_' + str(abs(hash(expr) + np.random.randint(500)))[-4:] except: name = 'result_' + str(abs(np.random.randint(500)))[-4:] return Symbol(name)
[docs] def get_class(self, name): """.""" cls = None if name in self._namespace['classes']: cls = self._namespace['classes'][name] return cls
[docs] def insert_class(self, cls): """.""" if isinstance(cls, ClassDef): name = str(cls.name) self._namespace['classes'][name] = cls else: raise TypeError('Expected A class definition ')
[docs] def get_function(self, name): """.""" # TODO shall we keep the elif in _imports? func = None if self._current: container = self._scope[self._current]['functions'] if name in container: return container[name] container = self._imports[self._current] if name in container: return container[name] if name in self._namespace['functions']: func = self._namespace['functions'][name] if self._current == name and not func.is_recursive: func = func.set_recursive() self._namespace['functions'][name] = func return func if name in self._imports: if not isinstance(self._imports[name], dict): return self._imports[name] return None
[docs] def insert_function(self, func): """.""" if isinstance(func, SympyFunction): self.insert_symbolic_function(func) elif isinstance(func, PythonFunction): self.insert_python_function(func) elif isinstance(func, (FunctionDef, Interface)): container = self._namespace['functions'] if isinstance(self._current, DottedName): name = self._current.name[0] container = self._scope[name]['functions'] container[str(func.name)] = func else: raise TypeError('Expected a Function definition')
[docs] def get_symbolic_function(self, name): """.""" # TODO shall we keep the elif in _imports? func = None if name in self._namespace['symbolic_functions']: func = self._namespace['symbolic_functions'][name] elif name in self._imports: func = self._imports[name] return func
[docs] def insert_symbolic_function(self, func): """.""" container = self._namespace['symbolic_functions'] if isinstance(self._current, DottedName): name = self._current.name[0] container = self._scope[name]['symbolic_functions'] if isinstance(func, SympyFunction): container[str(func.name)] = func elif isinstance(func, SymbolicAssign) and isinstance(func.rhs, Lambda): container[str(func.lhs)] = func.rhs else: raise TypeError('Expected a symbolic_function')
[docs] def get_python_function(self, name): """.""" # TODO shall we keep the elif in _imports? func = None if name in self._namespace['python_functions']: func = self._namespace['python_functions'][name] elif name in self._imports: func = self._imports[name] return func
[docs] def insert_python_function(self, func): """.""" container = self._namespace['python_functions'] if isinstance(self._current, DottedName): name = self._current.name[0] container = self._scope[name]['python_functions'] if isinstance(func, PythonFunction): container[str(func.name)] = func else: raise TypeError('Expected a python_function')
[docs] def get_macro(self, name): """.""" # TODO shall we keep the elif in _imports? macro = None if name in self._namespace['macros']: macro = self._namespace['macros'][name] # TODO uncomment # elif name in self._imports: # macro = self._imports[name] return macro
[docs] def insert_macro(self, macro): """.""" container = self._namespace['macros'] if isinstance(self._current, DottedName): name = self._current.name[0] container = self._scope[name]['macros'] if isinstance(macro, (MacroFunction, MacroVariable)): name = macro.name if isinstance(macro.name, DottedName): name = name.name[-1] container[str(name)] = macro else: raise TypeError('Expected a macro')
[docs] def remove_variable(self, name): """.""" # TODO improve to checkt each level of scoping if self._current: self._scope[self._current]['variables'].pop(name, None) else: self._namespace['variables'].pop(name, None)
[docs] def update_variable(self, var, **options): """.""" name = _get_variable_name(var).split(""".""") var = self.get_variable(name[0]) if len(name) > 1: name_ = _get_variable_name(var) for i in var.cls_base.attributes: if str(i.name) == name[1]: var = i name = name_ else: name = name[0] if var is None: raise ValueError('Undefined variable {name}'.format(name=name)) # TODO implement a method inside Variable d_var = self._infere_type(var) for (key, value) in options.items(): d_var[key] = value dtype = d_var.pop('datatype') var = Variable(dtype, name, **d_var) self.insert_variable(var, name) # TODO improve to insert in the right namespace return var
[docs] def get_header(self, name): """.""" if name in self.headers: return self.headers[name] return None
[docs] def get_class_construct(self, name): """Returns the class datatype for name.""" return self._namespace['cls_constructs'][name]
[docs] def set_class_construct(self, name, value): """Sets the class datatype for name.""" self._namespace['cls_constructs'][name] = value
[docs] def set_current_fun(self, name): """.""" if name: if self._current: name = DottedName(self._current, name) self._current = name self._scope[name] = {} self._scope[name]['variables'] = {} self._scope[name]['functions'] = {} self._scope[name]['symbolic_functions'] = {} self._scope[name]['python_functions'] = {} self._scope[name]['macros'] = {} self._scope[name]['imports'] = [] self._imports[name] = {} else: if isinstance(self._current, DottedName): # case of a function inside a function name = self._current.name[0] self._current = name
[docs] def insert_header(self, expr): """.""" if isinstance(expr, MethodHeader): self._namespace['headers'][str(expr.name)] = expr if isinstance(expr, FunctionHeader): self._namespace['headers'][str(expr.func)] = expr elif isinstance(expr, ClassHeader): self._namespace['headers'][str(expr.name)] = expr # create a new Datatype for the current class iterable = 'iterable' in expr.options with_construct = 'with' in expr.options dtype = DataTypeFactory(str(expr.name), '_name', is_iterable=iterable, is_with_construct=with_construct) self.set_class_construct(str(expr.name), dtype) else: raise TypeError('header of type{0} is not supported'.format(str(type(expr))))
def _collect_returns_stmt(self, ast): vars_ = [] for stmt in ast: if isinstance(stmt, (For, While)): vars_ += self._collect_returns_stmt(stmt.body) elif isinstance(stmt, If): vars_ += self._collect_returns_stmt(stmt.bodies) elif isinstance(stmt, Return): vars_ += [stmt] return vars_ def _fst_to_ast(self, stmt): """Creates AST from FST.""" # TODO - add settings to Errors # - line and column # - blocking errors errors = Errors() # ... def _treat_iterable(stmt): """ since redbaron puts the first comments after a block statement inside the block, we need to remove them. this is in particular the case when using openmp/openacc pragmas like #$ omp end loop """ ls = [self._fst_to_ast(i) for i in stmt] if isinstance(stmt, (list, ListNode)): return List(*ls) else: return Tuple(*ls) # ... if isinstance(stmt, ( RedBaron, LineProxyList, CommaProxyList, NodeList, TupleNode, ListNode, tuple, list, )): return _treat_iterable(stmt) elif isinstance(stmt, DottedAsNameNode): names = [] for a in stmt.value: names.append(strip_ansi_escape.sub('', a.value)) if len(names) == 1: return names[0] else: return DottedName(*names) elif isinstance(stmt, NameAsNameNode): if not isinstance(stmt.value, str): raise TypeError('Expecting a string') value = strip_ansi_escape.sub('', stmt.value) if not stmt.target: return value old = value new = self._fst_to_ast(stmt.target) # TODO improve if isinstance(old, str): old = old.replace("'", '') if isinstance(new, str): new = new.replace("'", '') return AsName(new, old) elif isinstance(stmt, DictNode): d = {} for i in stmt.value: if not isinstance(i, DictitemNode): raise PyccelSyntaxError('Expecting a DictitemNode') key = self._fst_to_ast(i.key) value = self._fst_to_ast(i.value) # sympy does not allow keys to be strings if isinstance(key, str): errors.report(SYMPY_RESTRICTION_DICT_KEYS, severity='error') d[key] = value return Dict(d) elif stmt is None: return Nil() elif isinstance(stmt, str): return repr(stmt) elif isinstance(stmt, StringNode): val = stmt.value if isinstance(stmt.parent,(RedBaron, DefNode)): return CommentBlock(val) return String(val) elif isinstance(stmt, IntNode): val = strip_ansi_escape.sub('', stmt.value) return Integer(val) elif isinstance(stmt, FloatNode): val = strip_ansi_escape.sub('', stmt.value) val = val[:20] if len(val)>20 else val return Float(val) elif isinstance(stmt, FloatExponantNode): val = strip_ansi_escape.sub('', stmt.value) val = val[:20] if len(val)>20 else val return Float(val) elif isinstance(stmt, ComplexNode): val = strip_ansi_escape.sub('', stmt.value) return sympify(val, locals=local_sympify) elif isinstance(stmt, AssignmentNode): lhs = self._fst_to_ast(stmt.target) rhs = self._fst_to_ast(stmt.value) if stmt.operator in ['+', '-', '*', '/']: expr = AugAssign(lhs, stmt.operator, rhs) else: expr = Assign(lhs, rhs) # we set the fst to keep track of needed information for errors expr.set_fst(stmt) return expr elif isinstance(stmt, NameNode): if stmt.value == 'None': return Nil() elif stmt.value == 'True': return true elif stmt.value == 'False': return false else: val = strip_ansi_escape.sub('', stmt.value) return Symbol(val) elif isinstance(stmt, ImportNode): if not isinstance(stmt.parent, (RedBaron, DefNode)): errors.report(PYCCEL_RESTRICTION_IMPORT, bounding_box=stmt.absolute_bounding_box, severity='error') if isinstance(stmt.parent, DefNode): errors.report(PYCCEL_RESTRICTION_IMPORT_IN_DEF, bounding_box=stmt.absolute_bounding_box, severity='error') # in an import statement, we can have seperate target by commas ls = self._fst_to_ast(stmt.value) ls = get_default_path(ls) expr = Import(ls) expr.set_fst(stmt) self.insert_import(expr) return expr elif isinstance(stmt, FromImportNode): if not isinstance(stmt.parent, (RedBaron, DefNode)): errors.report(PYCCEL_RESTRICTION_IMPORT, bounding_box=stmt.absolute_bounding_box, severity='error') source = self._fst_to_ast(stmt.value) if isinstance(source, DottedVariable): source = DottedName(*source.names) source = get_default_path(source) targets = [] for i in stmt.targets: s = self._fst_to_ast(i) if s == '*': errors.report(PYCCEL_RESTRICTION_IMPORT_STAR, bounding_box=stmt.absolute_bounding_box, severity='error') targets.append(s) if is_ignored_module(source): return EmptyLine() expr = Import(targets, source=source) expr.set_fst(stmt) self.insert_import(expr) return expr elif isinstance(stmt, DelNode): arg = self._fst_to_ast(stmt.value) return Del(arg) elif isinstance(stmt, UnitaryOperatorNode): target = self._fst_to_ast(stmt.target) if stmt.value == 'not': return Not(target) elif stmt.value == '+': return target elif stmt.value == '-': return -target elif stmt.value == '~': errors.report(PYCCEL_RESTRICTION_UNARY_OPERATOR, bounding_box=stmt.absolute_bounding_box, severity='error') else: raise PyccelSyntaxError('unknown/unavailable unary operator {node}'.format(node=type(stmt.value))) elif isinstance(stmt, (BinaryOperatorNode, BooleanOperatorNode)): first = self._fst_to_ast(stmt.first) second = self._fst_to_ast(stmt.second) if stmt.value == '+': return Add(first, second, evaluate=False) elif stmt.value == '*': if isinstance(first, (Tuple, List)): return Dlist(first[0], second) return Mul(first, second, evaluate=False) elif stmt.value == '-': if isinstance(stmt.second, BinaryOperatorNode) \ and isinstance(second, (Add, Mul)): args = second.args second = second._new_rawargs(-args[0], args[1]) else: second = Mul(-1, second) return Add(first, second, evaluate=False) elif stmt.value == '/': if isinstance(second, Mul) and isinstance(stmt.second, BinaryOperatorNode): args = list(second.args) second = Pow(args[0], -1, evaluate=False) second = Mul(second, args[1], evaluate=False) else: second = Pow(second, -1, evaluate=False) return Mul(first, second, evaluate=False) elif stmt.value == 'and': return And(first, second, evaluate=False) elif stmt.value == 'or': return Or(first, second, evaluate=False) elif stmt.value == '**': return Pow(first, second, evaluate=False) elif stmt.value == '//': if isinstance(second, Mul): args = second.args second = Pow(args[0], -1, evaluate=False) first = floor(Mul(first, second, evaluate=False)) return Mul(first, args[1], evaluate=False) else: second = Pow(second, -1, evaluate=False) return floor(Mul(first, second, evaluate=False)) elif stmt.value == '%': return Mod(first, second) else: raise PyccelSyntaxError('unknown/unavailable binary operator {node}'.format(node=type(stmt.value))) elif isinstance(stmt, ComparisonOperatorNode): if stmt.first == '==': return '==' elif stmt.first == '!=': return '!=' elif stmt.first == '<': return '<' elif stmt.first == '>': return '>' elif stmt.first == '<=': return '<=' elif stmt.first == '>=': return '>=' elif stmt.first == 'is': return 'is' else: raise PyccelSyntaxError('unknown comparison operator {}'.format(stmt.first)) elif isinstance(stmt, ComparisonNode): first = self._fst_to_ast(stmt.first) second = self._fst_to_ast(stmt.second) op = self._fst_to_ast(stmt.value) if op == '==': return Eq(first, second, evaluate=False) elif op == '!=': return Ne(first, second, evaluate=False) elif op == '<': return Lt(first, second, evaluate=False) elif op == '>': return Gt(first, second, evaluate=False) elif op == '<=': return Le(first, second, evaluate=False) elif op == '>=': return Ge(first, second, evaluate=False) elif op == 'is': return Is(first, second) else: raise PyccelSyntaxError('unknown/unavailable binary operator {node}'.format(node=type(op))) elif isinstance(stmt, PrintNode): expr = self._fst_to_ast(stmt.value) return Print(expr) elif isinstance(stmt, AssociativeParenthesisNode): return self._fst_to_ast(stmt.value) elif isinstance(stmt, DefArgumentNode): name = str(self._fst_to_ast(stmt.target)) name = strip_ansi_escape.sub('', name) arg = Argument(name) if stmt.value is None: return arg else: value = self._fst_to_ast(stmt.value) return ValuedArgument(arg, value) elif isinstance(stmt, ReturnNode): expr = Return(self._fst_to_ast(stmt.value)) expr.set_fst(stmt) return expr elif isinstance(stmt, PassNode): return Pass() elif isinstance(stmt, DefNode): # TODO check all inputs and which ones should be treated in stage 1 or 2 if isinstance(stmt.parent, ClassNode): cls_name = stmt.parent.name else: cls_name = None name = self._fst_to_ast(stmt.name) name = name.replace("'", '') name = strip_ansi_escape.sub('', name) arguments = self._fst_to_ast(stmt.arguments) results = [] local_vars = [] global_vars = [] header = None hide = False kind = 'function' imports = [] # TODO improve later decorators = {} for i in stmt.decorators: decorators.update(self._fst_to_ast(i)) # extract the types to construct a header if 'types' in decorators.keys(): types = [] results = [] container = types i = 0 n = len(decorators['types']) ls = decorators['types'] while i<len(ls) : arg = ls[i] if isinstance(arg, Symbol): arg = arg.name container.append(arg) elif isinstance(arg, String): arg = str(arg) arg = arg.replace("'", '') container.append(arg) elif isinstance(arg, ValuedArgument): arg_name = arg.name arg = arg.value container = results if not arg_name == 'results': msg = '> Wrong argument, given {}'.format(arg_name) raise NotImplementedError(msg) ls = arg if isinstance(arg, Tuple) else [arg] i = -1 else: msg = '> Wrong type, given {}'.format(type(arg)) raise NotImplementedError(msg) i = i+1 txt = '#$ header ' + name txt += '(' + ','.join(types) + ')' if results: txt += ' results(' + ','.join(results) + ')' header = hdr_parse(stmts=txt) if name in self.static_functions: header = header.to_static() body = stmt.value if 'sympy' in decorators.keys(): # TODO maybe we should run pylint here stmt.decorators.pop() func = SympyFunction(name, arguments, [], [stmt.__str__()]) self.insert_function(func) return EmptyLine() elif 'python' in decorators.keys(): # TODO maybe we should run pylint here stmt.decorators.pop() func = PythonFunction(name, arguments, [], [stmt.__str__()]) self.insert_function(func) return EmptyLine() else: body = self._fst_to_ast(body) func = FunctionDef( name, arguments, results, body, local_vars=local_vars, global_vars=global_vars, cls_name=cls_name, hide=hide, kind=kind, imports=imports, decorators=decorators, header=header) func.set_fst(stmt) return func elif isinstance(stmt, ClassNode): name = self._fst_to_ast(stmt.name) methods = [i for i in stmt.value if isinstance(i, DefNode)] methods = self._fst_to_ast(methods) attributes = methods[0].arguments parent = [i.value for i in stmt.inherit_from] expr = ClassDef(name=name, attributes=attributes, methods=methods, parent=parent) # we set the fst to keep track of needed information for errors expr.set_fst(stmt) return expr elif isinstance(stmt, AtomtrailersNode): return self._fst_to_ast(stmt.value) elif isinstance(stmt, GetitemNode): ch = stmt args = [] while isinstance(ch, GetitemNode): val = self._fst_to_ast(ch.value) if isinstance(val, Tuple): args += val else: args.insert(0, val) ch = ch.previous args = tuple(args) return args elif isinstance(stmt, SliceNode): upper = self._fst_to_ast(stmt.upper) lower = self._fst_to_ast(stmt.lower) if not isinstance(upper, Nil) and not isinstance(lower, Nil): return Slice(lower, upper) elif not isinstance(lower, Nil): return Slice(lower, None) elif not isinstance(upper, Nil): return Slice(None, upper) else: return Slice(None, None) elif isinstance(stmt, DotProxyList): n = 0 ls = [] while n < len(stmt): var = self._fst_to_ast(stmt[n]) while n < len(stmt) and not isinstance(stmt[n].next, DotNode): n = n + 1 if n == len(stmt): n = n - 1 if isinstance(stmt[n], GetitemNode): args = self._fst_to_ast(stmt[n]) var = IndexedBase(var)[args] elif isinstance(stmt[n], CallNode): var = self._fst_to_ast(stmt[n]) ls.append(var) n = n + 1 if len(ls) == 1: expr = ls[0] else: n = 0 var = DottedVariable(ls[0], ls[1]) n = 2 while n < len(ls): var = DottedVariable(var, ls[n]) n = n + 1 expr = var return expr elif isinstance(stmt, CallNode): if len(stmt.value) > 0 and isinstance(stmt.value[0], ArgumentGeneratorComprehensionNode): return self._fst_to_ast(stmt.value[0]) args = self._fst_to_ast(stmt.value) f_name = str(stmt.previous.value) f_name = strip_ansi_escape.sub('', f_name) if len(args) == 0: args = (Nil(), ) func = Function(f_name)(*args) return func elif isinstance(stmt, CallArgumentNode): target = stmt.target val = self._fst_to_ast(stmt.value) if target: target = self._fst_to_ast(target) return ValuedArgument(target, val) return val elif isinstance(stmt, DecoratorNode): name = strip_ansi_escape.sub('', stmt.value.dumps()) args = [] if stmt.call: args = [self._fst_to_ast(i) for i in stmt.call.value] return {name: args} elif isinstance(stmt, ForNode): iterator = self._fst_to_ast(stmt.iterator) iterable = self._fst_to_ast(stmt.target) body = list(self._fst_to_ast(stmt.value)) expr = For(iterator, iterable, body, strict=False) expr.set_fst(stmt) return expr elif isinstance(stmt, ComprehensionLoopNode): iterator = self._fst_to_ast(stmt.iterator) iterable = self._fst_to_ast(stmt.target) ifs = stmt.ifs expr = For(iterator, iterable, [], strict=False) expr.set_fst(stmt) return expr elif isinstance(stmt, ArgumentGeneratorComprehensionNode): result = self._fst_to_ast(stmt.result) generators = self._fst_to_ast(stmt.generators) parent = stmt.parent.parent.parent if isinstance(parent, AssignmentNode): lhs = self._fst_to_ast(parent.target) name = strip_ansi_escape.sub('', parent.value[0].value) cond = False else: lhs = self.create_variable(result) name = stmt.parent.parent name = strip_ansi_escape.sub('', name.value[0].value) cond = True body = result if name == 'sum': body = AugAssign(lhs, '+', body) else: body = Function(name)(lhs, body) body = Assign(lhs, body) target = body body.set_fst(parent) indexes = [] generators = list(generators) while len(generators) > 0: indexes.append(generators[-1].target) generators[-1].insert2body(body) body = generators.pop() indexes = indexes[::-1] body = [body] if name == 'sum': expr = FunctionalSum(body, target, indexes, None) elif name == 'min': expr = FunctionalMin(body, target, indexes, None) elif name == 'max': expr = FunctionalMax(body, target, indexes, None) else: raise NotImplementedError('TODO') expr.set_fst(stmt) if cond: expr = Assign(lhs, expr) expr.set_fst(stmt) return expr elif isinstance(stmt, IfelseblockNode): args = self._fst_to_ast(stmt.value) return If(*args) elif isinstance(stmt, (IfNode, ElifNode)): test = self._fst_to_ast(stmt.test) body = self._fst_to_ast(stmt.value) return Tuple(test, body) elif isinstance(stmt, ElseNode): test = True body = self._fst_to_ast(stmt.value) return Tuple(test, body) elif isinstance(stmt, TernaryOperatorNode): test1 = self._fst_to_ast(stmt.value) first = self._fst_to_ast(stmt.first) second = self._fst_to_ast(stmt.second) args = [Tuple(test1, [first]), Tuple(True, [second])] expr = IfTernaryOperator(*args) expr.set_fst(stmt) return expr elif isinstance(stmt, WhileNode): test = self._fst_to_ast(stmt.test) body = self._fst_to_ast(stmt.value) return While(test, body) elif isinstance(stmt, AssertNode): expr = self._fst_to_ast(stmt.value) return Assert(expr) elif isinstance(stmt, EndlNode): return NewLine() elif isinstance(stmt, CommentNode): # if annotated comment if stmt.value.startswith('#$'): env = stmt.value[2:].lstrip() if env.startswith('omp'): txt = reconstruct_pragma_multilines(stmt) return omp_parse(stmts=txt) elif env.startswith('acc'): txt = reconstruct_pragma_multilines(stmt) return acc_parse(stmts=txt) elif env.startswith('header'): txt = reconstruct_pragma_multilines(stmt) expr = hdr_parse(stmts=txt) if isinstance(expr, MetaVariable): # a metavar will not appear in the semantic stage. # but can be used to modify the ast self._metavars[str(expr.name)] = str(expr.value) # return NewLine() expr = EmptyLine() else: expr.set_fst(stmt) return expr else: # TODO an info should be reported saying that either we # found a multiline pragma or an invalid pragma statement return NewLine() else: # errors.report(PYCCEL_INVALID_HEADER, # bounding_box=stmt.absolute_bounding_box, # severity='error') # TODO improve txt = stmt.value[1:].lstrip() return Comment(txt) elif isinstance(stmt, BreakNode): return Break() elif isinstance(stmt, ContinueNode): return Continue() elif isinstance(stmt, StarNode): return '*' elif isinstance(stmt, LambdaNode): expr = self._fst_to_ast(stmt.value) args = [] for i in stmt.arguments: var = self._fst_to_ast(i.name) args += [var] return Lambda(args, expr) elif isinstance(stmt, WithNode): domain = self._fst_to_ast(stmt.contexts[0].value) body = self._fst_to_ast(stmt.value) settings = None return With(domain, body, settings) elif isinstance(stmt, ListComprehensionNode): import numpy as np result = self._fst_to_ast(stmt.result) generators = list(self._fst_to_ast(stmt.generators)) lhs = self._fst_to_ast(stmt.parent.target) index = self.create_variable(lhs) if isinstance(result, (Tuple, list, tuple)): rank = len(np.shape(result)) else: rank = 0 args = [Slice(None, None)] * rank args.append(index) target = IndexedBase(lhs)[args] target = Assign(target, result) assign1 = Assign(index, 0) assign1.set_fst(stmt) target.set_fst(stmt) generators[-1].insert2body(target) assign2 = Assign(index, index + 1) assign2.set_fst(stmt) generators[-1].insert2body(assign2) indexes = [generators[-1].target] while len(generators) > 1: F = generators.pop() generators[-1].insert2body(F) indexes.append(generators[-1].target) indexes = indexes[::-1] return FunctionalFor([assign1, generators[-1]], target, indexes, index) elif isinstance(stmt, (ExceptNode, FinallyNode, TryNode)): # this is a blocking error, since we don't want to convert the try body errors.report(PYCCEL_RESTRICTION_TRY_EXCEPT_FINALLY, bounding_box=stmt.absolute_bounding_box, severity='error') elif isinstance(stmt, RaiseNode): errors.report(PYCCEL_RESTRICTION_RAISE, bounding_box=stmt.absolute_bounding_box, severity='error') elif isinstance(stmt, (YieldNode, YieldAtomNode)): errors.report(PYCCEL_RESTRICTION_YIELD, bounding_box=stmt.absolute_bounding_box, severity='error') else: raise PyccelSyntaxError('{node} not yet available'.format(node=type(stmt))) def _infere_type(self, expr, **settings): """ type inference for expressions """ # TODO - add settings to Errors # - line and column # - blocking errors errors = Errors() verbose = settings.pop('verbose', False) if verbose: print ('*** type inference for : ', type(expr)) d_var = {} # d_var['datatype'] = None d_var['datatype'] = NativeSymbol() d_var['allocatable'] = None d_var['shape'] = () d_var['rank'] = 0 d_var['is_pointer'] = None d_var['is_target'] = None d_var['is_polymorphic'] = None d_var['is_optional'] = None d_var['cls_base'] = None d_var['cls_parameters'] = None d_var['precision'] = 0 # TODO improve => put settings as attribut of Parser DEFAULT_FLOAT = settings.pop('default_float', 'real') if isinstance(expr, type(None)): return d_var elif isinstance(expr, (Integer, int)): d_var['datatype'] = 'int' d_var['allocatable'] = False d_var['rank'] = 0 d_var['precision'] = 4 return d_var elif isinstance(expr, (Float, float)): d_var['datatype'] = DEFAULT_FLOAT d_var['allocatable'] = False d_var['rank'] = 0 d_var['precision'] = 8 return d_var elif isinstance(expr, String): d_var['datatype'] = 'str' d_var['allocatable'] = False d_var['rank'] = 0 return d_var elif isinstance(expr, ImaginaryUnit): d_var['datatype'] = 'complex' d_var['allocatable'] = False d_var['rank'] = 0 d_var['precision'] = 8 return d_var elif isinstance(expr, Variable): d_var['datatype'] = expr.dtype d_var['allocatable'] = expr.allocatable d_var['shape'] = expr.shape d_var['rank'] = expr.rank d_var['cls_base'] = expr.cls_base d_var['is_pointer'] = expr.is_pointer d_var['is_polymorphic'] = expr.is_polymorphic d_var['is_optional'] = expr.is_optional d_var['is_target'] = expr.is_target d_var['order'] = expr.order d_var['precision'] = expr.precision return d_var elif isinstance(expr, (BooleanTrue, BooleanFalse)): d_var['datatype'] = NativeBool() d_var['allocatable'] = False d_var['is_pointer'] = False d_var['rank'] = 0 return d_var elif isinstance(expr, IndexedElement): d_var['datatype'] = expr.dtype name = _get_name(expr) var = self.get_variable(name) if var is None: raise ValueError('Undefined variable {name}'.format(name=name)) d_var['datatype'] = str_dtype(var.dtype) if sympy_iterable(var.shape): shape = [] for (s, i) in zip(var.shape, expr.indices): if isinstance(i, Slice): shape.append(i) else: shape = () rank = max(0, var.rank - expr.rank) if rank > 0: d_var['allocatable'] = var.allocatable d_var['is_pointer'] = var.is_pointer d_var['shape'] = shape d_var['rank'] = rank d_var['precision'] = var.precision return d_var elif isinstance(expr, IndexedVariable): name = _get_name(expr) var = self.get_variable(name) if var is None: raise ValueError('Undefined variable {name}'.format(name=name)) d_var['datatype'] = var.dtype d_var['allocatable'] = var.allocatable d_var['shape'] = var.shape d_var['rank'] = var.rank d_var['precision'] = var.precision return d_var elif isinstance(expr, Range): d_var['datatype'] = NativeRange() d_var['allocatable'] = False d_var['shape'] = () d_var['rank'] = 0 d_var['cls_base'] = expr # TODO: shall we keep it? return d_var elif isinstance(expr, Is): d_var['datatype'] = NativeBool() d_var['allocatable'] = False d_var['is_pointer'] = False d_var['rank'] = 0 return d_var elif isinstance(expr, DottedVariable): if isinstance(expr.lhs, DottedVariable): self._current_class = expr.lhs.rhs.cls_base else: self._current_class = expr.lhs.cls_base d_var = self._infere_type(expr.rhs) self._current_class = None return d_var elif isinstance(expr, Lambda): d_var['datatype'] = NativeSymbol() d_var['allocatable'] = False d_var['is_pointer'] = False d_var['rank'] = 0 return d_var elif isinstance(expr, Application): name = _get_name(expr) func = self.get_function(name) if isinstance(func, FunctionDef): d_var = self._infere_type(func.results[0], **settings) elif name in ['Zeros', 'Ones', 'Empty']: d_var = {} d_var['datatype'] = expr.dtype d_var['allocatable'] = True d_var['shape'] = expr.shape d_var['rank'] = expr.rank d_var['is_pointer'] = False d_var['order'] = expr.order elif name in ['Shape']: d_var = {} d_var['datatype'] = expr.dtype d_var['shape'] = expr.shape d_var['rank'] = expr.rank d_var['allocatable'] = False d_var['is_pointer'] = False elif name in ['Array']: dvar = self._infere_type(expr.arg, **settings) if expr.dtype: dvar['datatype'] = expr.dtype dvar['precision'] = expr.precision dvar['datatype'] = str_dtype(dvar['datatype']) d_var = {} d_var['allocatable'] = True d_var['shape'] = dvar['shape'] d_var['rank'] = dvar['rank'] d_var['is_pointer'] = False d_var['datatype'] = 'ndarray' + dvar['datatype'] d_var['precision'] = dvar['precision'] elif name in ['Len', 'Sum', 'Rand', 'Min', 'Max']: d_var = {} d_var['datatype'] = sp_dtype(expr) d_var['rank'] = 0 d_var['allocatable'] = False d_var['is_pointer'] = False elif name in ['Int','Int32','Int64','Real', 'Float32','Float64','Complex', 'Complex128','Complex64']: d_var = {} d_var['datatype'] = sp_dtype(expr) d_var['rank'] = 0 d_var['allocatable'] = False d_var['is_pointer'] = False d_var['precision'] = expr.precision elif name in ['Mod']: # Determine output type/rank/shape # TODO [YG, 10.10.2018]: use Numpy broadcasting rules d_vars = [self._infere_type(arg,**settings) for arg in expr.args] i = 0 if (d_vars[0]['rank'] >= d_vars[1]['rank']) else 1 d_var = {} d_var['datatype' ] = d_vars[i]['datatype'] d_var['rank' ] = d_vars[i]['rank'] d_var['shape' ] = d_vars[i]['shape'] d_var['allocatable'] = d_vars[i]['allocatable'] d_var['is_pointer' ] = False d_var['precision'] = d_vars[i].pop('precision',4) elif name in [ 'Abs', 'sin', 'cos', 'exp', 'log', 'csc', 'cos', 'sec', 'tan', 'cot', 'asin', 'acsc', 'acos', 'asec', 'atan', 'acot', 'atan2', ]: d_var = self._infere_type(expr.args[0], **settings) d_var['datatype'] = sp_dtype(expr) elif name in ['ZerosLike']: d_var = self._infere_type(expr.rhs, **settings) elif name in ['floor']: d_var = self._infere_type(expr.args[0], **settings) d_var['datatype'] = 'int' if expr.args[0].is_complex and not expr.args[0].is_integer: d_var['precision'] = d_var['precision']//2 else: d_var['precision'] = 4 else: raise NotImplementedError('TODO') return d_var elif isinstance(expr, Expr): cls = (Application, DottedVariable, Variable, IndexedVariable,IndexedElement) atoms = _atomic(expr,cls) ds = [self._infere_type(i, **settings) for i in atoms] #TODO we should also look for functions call #to collect info about precision and shapes later when we allow # vectorised operations # we only look for atomic expression of type Variable # because we don't allow functions that returns an array in an expression allocatables = [d['allocatable'] for d in ds] pointers = [d['is_pointer'] or d['is_target'] for d in ds] ranks = [d['rank'] for d in ds] shapes = [d['shape'] for d in ds] precisions = [d['precision'] for d in ds] if all(i.is_integer for i in atoms): if expr.is_complex and not expr.is_integer: precisions.append(8) # TODO improve # ... only scalars and variables of rank 0 can be handled if any(ranks): r_min = min(ranks) r_max = max(ranks) if not r_min == r_max: if not r_min == 0: raise ValueError('cannot process arrays of different ranks.' ) rank = r_max else: rank = 0 shape = () for s in shapes: if s: shape = s # ... d_var['datatype'] = sp_dtype(expr) d_var['allocatable'] = any(allocatables) d_var['is_pointer'] = any(pointers) d_var['shape'] = shape d_var['rank'] = rank if len(precisions)>0: d_var['precision'] = max(precisions) else: if d_var['datatype']=='int': d_var['precision'] = 4 else: d_var['precision'] = 8 return d_var elif isinstance(expr, (tuple, list, List, Tuple)): import numpy d = self._infere_type(expr[0], **settings) # TODO must check that it is consistent with pyccel's rules d_var['datatype'] = d['datatype'] d_var['rank'] = d['rank'] + 1 d_var['shape'] = numpy.shape(expr) # TODO improve d_var['allocatable'] = d['allocatable'] if isinstance(expr, List): d_var['is_target'] = True dtype = str_dtype(d['datatype']) if dtype == 'integer': d_var['datatype'] = NativeIntegerList() elif dtype == 'real': d_var['datatype'] = NativeRealList() elif dtype == 'complex': d_var['datatype'] = NativeComplexList() else: raise NotImplementedError('TODO') return d_var elif isinstance(expr, Concatinate): import operator d_vars = [self._infere_type(a, **settings) for a in expr.args] ls = any(d['is_pointer'] or d['is_target'] for d in d_vars) if ls: shapes = [d['shape'] for d in d_vars if d['shape']] shapes = zip(*shapes) shape = tuple(sum(s) for s in shapes) if not shape: shape = (sum(map(Len,expr.args)),) d_vars[0]['shape'] = shape d_vars[0]['rank'] = 1 d_vars[0]['is_target'] = True d_vars[0]['is_pointer'] = False else: d_vars[0]['datatype'] = 'str' return d_vars[0] if not (d_var_left['datatype'] == 'str' or d_var_right['datatype'] == 'str'): d_var_left['shape'] = tuple(map(operator.add, d_var_right['shape'], d_var_left['shape'])) return d_var_left elif isinstance(expr, ValuedArgument): return self._infere_type(expr.value) elif isinstance(expr, Dlist): import numpy d = self._infere_type(expr.val, **settings) # TODO must check that it is consistent with pyccel's rules d_var['datatype'] = d['datatype'] d_var['rank'] = d['rank'] + 1 d_var['shape'] = (expr.length, ) # TODO improve d_var['allocatable'] = False d_var['is_pointer'] = True return d_var elif isinstance(expr, GC): return self._infere_type(expr.target, **settings) else: raise NotImplementedError('{expr} not yet available'.format(expr=type(expr))) def _annotate(self, expr, **settings): """Annotates the AST. IndexedVariable atoms are only used to manipulate expressions, we then, always have a Variable in the namespace.""" # TODO - add settings to Errors # - line and column # - blocking errors errors = Errors() if isinstance(expr, (list, tuple, Tuple)): ls = [] for i in expr: a = self._annotate(i, **settings) ls.append(a) if isinstance(expr, List): return List(*ls) else: return Tuple(*ls) elif isinstance(expr, (CodeBlock, Nil, ValuedArgument, EmptyLine, NewLine,Break, Continue, Pass, Comment, CommentBlock,AnnotatedComment, Integer, Float, String, ImaginaryUnit, BooleanTrue, BooleanFalse)): return expr elif isinstance(expr, int): return Integer(expr) elif isinstance(expr, float): return Float(expr) elif isinstance(expr, complex): return sympify(expr, locals=local_sympify) elif isinstance(expr, NumberSymbol) or isinstance(expr, Number): return Float(float(expr)) elif isinstance(expr, Variable): name = expr.name var = self.get_variable(name) if var is None: # TODO ERROR not tested yet errors.report(UNDEFINED_VARIABLE, symbol=name, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) return var elif isinstance(expr, str): return repr(expr) elif isinstance(expr, (IndexedVariable, IndexedBase)): # an indexed variable is only defined if the associated variable is in # the namespace # TODO we don't actualy pass by this condition should we remove it and use the other ? name = str(expr.name) var = self.get_variable(name) if var is None: # TODO ERROR not tested yet errors.report(UNDEFINED_VARIABLE, symbol=name, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) dtype = var.dtype shape = var.shape prec = var.precision order = var.order rank = var.rank return IndexedVariable(name, dtype=dtype, shape=shape,prec=prec,order=order,rank=rank) elif isinstance(expr, (IndexedElement, Indexed)): name = _get_name(expr) var = self.get_variable(name) if var is None: errors.report(UNDEFINED_INDEXED_VARIABLE, symbol=name, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) # TODO check consistency of indices with shape/rank args = list(expr.indices) if var.order == 'C': args.reverse() args = tuple(args) # case of Pyccel ast Variable, IndexedVariable # if not possible we use symbolic objects if hasattr(var, 'dtype'): dtype = var.dtype shape = var.shape prec = var.precision order = var.order rank = var.rank return IndexedVariable(name, dtype=dtype, shape=shape,prec=prec,order=order,rank=rank).__getitem__(*args) else: return IndexedBase(name).__getitem__(args) elif isinstance(expr, Symbol): name = _get_name(expr) var = self.get_variable(name) if var is None: var = self.get_function(name) if var is None: var = self.get_symbolic_function(name) if var is None: errors.report(UNDEFINED_VARIABLE, symbol=name, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) return var elif isinstance(expr, DottedVariable): first = self._annotate(expr.lhs) rhs_name = _get_name(expr.rhs) attr_name = [] if first.cls_base: attr_name = [i.name for i in first.cls_base.attributes] name = None if isinstance(expr.rhs, Symbol) and not expr.rhs.name \ in attr_name: for i in first.cls_base.methods: if str(i.name) == expr.rhs.name and 'property' \ in i.decorators.keys(): second = Function(expr.rhs.name)(Nil()) expr = DottedVariable(first, second) d_var = self._infere_type(i.results[0], **settings) dtype = d_var['datatype'] assumptions = {str_dtype(dtype): True} expr._assumptions = StdFactKB(assumptions) expr._assumptions._generator = \ assumptions.copy() return expr if not isinstance(expr.rhs, Application): macro = self.get_macro(rhs_name) if macro: return macro.master self._current_class = first.cls_base second = self._annotate(expr.rhs, **settings) self._current_class = None else: macro = self.get_macro(rhs_name) if not macro is None: master = macro.master name = macro.name master_args = macro.master_arguments args = expr.rhs.args args = [expr.lhs] + list(args) args = [self._annotate(i, **settings) for i in args] args = macro.apply(args) if isinstance(master, FunctionDef): return Subroutine(str(master.name))(*args) else: raise NotImplementedError('TODO case of interface' ) args = [self._annotate(arg, **settings) for arg in expr.rhs.args] for i in first.cls_base.methods: if str(i.name.name) == rhs_name: if len(i.results) == 1: second = Function(i.name.name)(*args) d_var = self._infere_type(i.results[0], **settings) dtype = d_var['datatype'] assumptions = {str_dtype(dtype): True} expr._assumptions = StdFactKB(assumptions) expr._assumptions._generator = \ assumptions.copy() elif len(i.results) == 0: second = Subroutine(i.name.name)(*args) elif len(i.results) > 1: raise NotImplementedError('TODO case multiple return variables' ) expr = DottedVariable(first, second) return expr return DottedVariable(first, second) elif isinstance(expr, ( Add, Mul, Pow,sp_Pow, And, Or, Eq, Ne, Lt, Gt, Le, Ge, )): # we reconstruct the arithmetic expressions using the annotated # arguments if isinstance(expr, Add): atoms_str = _atomic(expr,String) atoms_ls = _atomic(expr,List) cls =(Symbol, DottedVariable) atoms = _atomic(expr, cls) atoms = [self._annotate(a, **settings) for a in atoms] atoms = [a.rhs if isinstance(a, DottedVariable) else a for a in atoms] atoms = [self._infere_type(a , **settings) for a in atoms] atoms = [a['is_pointer'] for a in atoms] args = [self._annotate(a, **settings) for a in expr.args] temp = self.create_variable(expr) if any(atoms) or atoms_ls: return Concatinate(args, True) elif atoms_str: return Concatinate(args, False) args = expr.args # we treat the first element a = args[0] a_new = self._annotate(a, **settings) expr_new = a_new # then we treat the rest for a in args[1:]: a_new = self._annotate(a, **settings) if isinstance(expr, Add): expr_new = Add(a_new , expr_new , evaluate=False) elif isinstance(expr, Mul): expr_new = Mul(expr_new, a_new, evaluate=False) elif isinstance(expr, (Pow, sp_Pow)): expr_new = Pow(expr_new, a_new, evaluate=False) elif isinstance(expr, And): expr_new = And(expr_new, a_new, evaluate=False) elif isinstance(expr, Or): expr_new = Or(expr_new, a_new) elif isinstance(expr, Eq): expr_new = Eq(expr_new, a_new, evaluate=False) elif isinstance(expr, Ne): expr_new = Ne(expr_new, a_new, evaluate=False) elif isinstance(expr, Lt): expr_new = Lt(expr_new, a_new, evaluate=False) elif isinstance(expr, Le): expr_new = Le(expr_new, a_new, evaluate=False) elif isinstance(expr, Gt): expr_new = Gt(expr_new, a_new, evaluate=False) elif isinstance(expr, Ge): expr_new = Ge(expr_new, a_new, evaluate=False) if not expr_new.is_integer and expr_new.is_real: expr_new = int2float(expr_new) return expr_new.doit(deep=False) elif isinstance(expr, Lambda): expr_names = set(map(str, expr.expr.atoms(Symbol))) var_names = map(str, expr.variables) if len(expr_names.difference(var_names)) > 0: raise ValueError('Unknown variables in lambda definition ' ) funcs = expr.expr.atoms(Function) for func in funcs: name = _get_name(func) f = self.get_symbolic_function(name) if f is None: raise ValueError('Unknown function in lambda definition' ) else: f = f(*func.args) expr_new = expr.expr.subs(func, f) expr = Lambda(expr.variables, expr_new) return expr elif isinstance(expr, Application): # ... DEBUG name = _get_name(expr) func = self.get_function(name) args = list(expr.args) for (i, arg) in enumerate(expr.args): if isinstance(arg, IfTernaryOperator): new_args1 = args[:i] + list(arg.args[0][1]) \ + args[i + 1:] func1 = Function(name)(*new_args1) new_args2 = args[:i] + list(arg.args[1][1]) \ + args[i + 1:] func2 = Function(name)(*new_args2) expr = IfTernaryOperator(Tuple(arg.args[0][0], [func1]), Tuple(arg.args[1][0], [func2])) expr.set_fst(arg.fst) return self._annotate(expr, **settings) args = [] stmts = [] for i in expr.args: if isinstance(i, Assign): args.append(i.lhs) stmts.append(i) else: args.append(i) for i in range(len(stmts)): stmts[i] = self._annotate(stmts[i], **settings) args = [self._annotate(arg, **settings) for arg in args] if name == 'lambdify': args = self.get_symbolic_function(str(expr.args[0])) F = pyccel_builtin_function(expr, args) if F: if len(stmts) > 0: stmts.append(F) return CodeBlock(stmts) return F elif name in self._namespace['cls_constructs'].keys(): # TODO improve the test # we must not invoke the namespace like this cls = self.get_class(name) d_methods = cls.methods_as_dict method = d_methods.pop('__init__', None) if method is None: # TODO improve case of class with the no __init__ errors.report(UNDEFINED_INIT_METHOD, symbol=name, bounding_box=self.bounding_box, severity='error', blocker=True) args = expr.args m_args = method.arguments[1:] # we delete the self arg # TODO check compatibility # TODO treat parametrized arguments. expr = ConstructorCall(method, args, cls_variable=None) if len(stmts) > 0: stmts.append(expr) return CodeBlock(stmts) return expr else: # first we check if it is a macro, in this case, we will create # an appropriate FunctionCall macro = self.get_macro(name) if not macro is None: args = [self._annotate(i, **settings) for i in args] func = macro.master name = _get_name(func.name) args = macro.apply(args) else: func = self.get_function(name) if func is None: errors.report(UNDEFINED_FUNCTION, symbol=name, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) else: if not isinstance(func, (FunctionDef, Interface)): expr = func(*args) if len(stmts) > 0: stmts.append(expr) return CodeBlock(stmts) return expr else: if 'inline' in func.decorators.keys(): return inline(func,args) if isinstance(func, FunctionDef): results = func.results f_args = func.arguments elif isinstance(func, Interface): arg_dvar = [self._infere_type(i, **settings) for i in args] f_dvar = [[self._infere_type(j, **settings) for j in i.arguments] for i in func.functions] j = -1 for i in f_dvar: j += 1 found = True for (idx, dt) in enumerate(arg_dvar): dtype1 = str_dtype(dt['datatype']) dtype2 = str_dtype(i[idx]['datatype']) found = found and (dtype1 in dtype2 or dtype2 in dtype1) found = found and dt['rank'] \ == i[idx]['rank'] if found: break if found: results = func.functions[j].results f_args = func.functions[j].arguments else: raise SystemExit('function not found in the interface' ) if func.hide: # hide means here print the real function's name name = str(func.functions[j].name) # add the messing argument in the case of optional arguments if not len(args) == len(f_args): n = len(args) for i in f_args[n:]: if not isinstance(i, ValuedVariable): raise TypeError('Expecting a valued variable') if not isinstance(i.value, Nil): args.append(ValuedArgument(i.name, i.value)) if len(results) == 1: expr = Function(name)(*args) d_var = self._infere_type(results[0], *settings) dtype = d_var['datatype'] assumptions = {str_dtype(dtype): True} expr._assumptions = StdFactKB(assumptions) expr._assumptions._generator = \ assumptions.copy() elif len(results) == 0: expr = Subroutine(name)(*args) if len(stmts) > 0: stmts.append(expr) return CodeBlock(stmts) return expr elif len(results) > 1: expr = Function(name)(*args) if len(stmts) > 0: stmts.append(expr) return CodeBlock(stmts) return expr if len(stmts) > 0: stmts.append(expr) return CodeBlock(stmts) return expr elif isinstance(expr, Expr): raise NotImplementedError('{expr} not yet available'.format(expr=type(expr))) elif isinstance(expr, (Assign, AugAssign)): # TODO unset position at the end of this part if expr.fst: self._bounding_box = expr.fst.absolute_bounding_box else: msg = \ 'Found a node without fst member ({})'.format(type(expr)) raise PyccelSemanticError(msg) rhs = expr.rhs lhs = expr.lhs assigns = None if isinstance(rhs, Application): name = _get_name(rhs) macro = self.get_macro(name) if not macro is None: # TODO check types from FunctionDef master = macro.master name = _get_name(master.name) # all terms in lhs must be already declared and available # the namespace # TODO improve if not sympy_iterable(lhs): lhs = [lhs] results = [] for a in lhs: _name = _get_name(a) var = self.get_variable(_name) if var is None: errors.report(UNDEFINED_VARIABLE, symbol=_name, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) results.append(var) # ... args = [self._annotate(i, **settings) for i in rhs.args] args = macro.apply(args, results=results) if isinstance(master, FunctionDef): return Subroutine(name)(*args) else: raise NotImplementedError('TODO treate interface case' ) if isinstance(rhs, DottedVariable): var = rhs.rhs name = _get_name(var) macro = self.get_macro(name) if not macro is None: master = macro.master if isinstance(macro, MacroVariable): self.insert_variable(master) rhs = master else: name = macro.name master_args = macro.master_arguments if not sympy_iterable(lhs): lhs = [lhs] results = [] for a in lhs: _name = _get_name(a) var = self.get_variable(_name) if var is None: errors.report(UNDEFINED_VARIABLE, symbol=_name, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) results.append(var) # ... args = rhs.rhs.args args = [rhs.lhs] + list(args) args = [self._annotate(i, **settings) for i in args] args = macro.apply(args, results=results) # TODO treate interface case if isinstance(master, FunctionDef): return Subroutine(str(master.name))(*args) else: raise NotImplementedError('TODO') if isinstance(rhs, (Mul, Add, Pow)): ls = _atomic(rhs, Assign) if len(ls) > 0: stmts = [] for i in ls: rhs = rhs.subs(i, i.lhs) stmts.append(i.rhs) for i in range(len(stmts)): stmts[i] = self._annotate(stmts[i], **settings) stmt = Assign(lhs, rhs) stmt.set_fst(expr.fst) stmt = self._annotate(stmt, **settings) stmts.append(stmt) return CodeBlock(stmts) if isinstance(rhs, (Min, Max, Mul, Add, Pow)) \ and len(rhs.atoms(Summation)) > 0: ls = list(rhs.atoms(Summation)) ls += [rhs] (ls, m) = cse(ls) (vars_old, stmts) = map(list, zip(*ls)) vars_new = [] free_gl = rhs.free_symbols free_gl.update(rhs.atoms(IndexedBase)) free_gl.update(vars_old) stmts.append(rhs) for i in range(len(stmts) - 1): free = stmts[i].free_symbols free = free.difference(free_gl) free = list(free) var = self.create_variable(stmts[i]) if len(free) > 0: var = IndexedBase(var)[free] vars_new.append(var) for i in range(len(stmts) - 1): stmts[i + 1] = stmts[i + 1].replace(vars_old[i], vars_new[i]) stmts[-1] = stmts[-1].replace(stmts[i], vars_new[i]) allocate = [] for i in range(len(stmts) - 1): stmts[i] = Assign(vars_new[i], stmts[i]) stmts[i].set_fst(expr.fst) if isinstance(vars_new[i], Indexed): ind = vars_new[i].indices tp = list(stmts[i + 1].atoms(Tuple)) size = None size = [None] * len(ind) for (j, k) in enumerate(ind): for t in tp: if k == t[0]: size[j] = t[2] - t[1] + 1 break if not all(size): raise ValueError('Unable to find range of index' ) name = _get_name(vars_new[i].base) var = Symbol(name) stmt = Assign(var, Function('zeros')(size[0])) stmt.set_fst(expr.fst) allocate.append(stmt) stmts[i] = For(ind[0], Function('range' )(size[0]), [stmts[i]], strict=False) stmts[-1] = Assign(expr.lhs, stmts[-1]) stmts[-1].set_fst(expr.fst) container = self._imports if self._current: conainter = container[self._current] container['zeros'] = Zeros allocate = [self._annotate(i, **settings) for i in allocate] stmts = [self._annotate(i, **settings) for i in stmts] return CodeBlock(allocate + stmts) if isinstance(rhs, Summation): index = rhs.args[1] target = Function('range')(index[1], index[2]) lhs = expr.lhs body = AugAssign(lhs, '+', rhs.args[0]) body.set_fst(expr.fst) body = self._annotate(body, **settings) stmt = For(index[0], target, [body], strict=False) stmt.set_fst(expr.fst) stmt = FunctionalSum([stmt], body, [], None) stmt.set_fst(expr.fst) rhs = self._annotate(stmt, **settings) return rhs elif isinstance(rhs, (Assign, AugAssign)): rhs_ = self._annotate(rhs, **settings) if isinstance(rhs_, FunctionalSum): stmt = AugAssign(expr.lhs, '+', rhs.lhs) elif isinstance(rhs_, GC): stmt = Function(rhs_.name)(expr.lhs, rhs.lhs) stmt = Assign(expr.lhs, stmt) else: raise NotImplementedError('TODO') stmt.set_fst(expr.fst) stmt = self._annotate(stmt, **settings) return CodeBlock([rhs_, stmt]) # ....... rhs = self._annotate(rhs, **settings) # ....... if isinstance(rhs, If): args = rhs.args new_args = [] for arg in args: if len(arg[1]) != 1: raise ValueError('IfTernaryOperator body must be of length 1' ) result = arg[1][0] if isinstance(expr, Assign): body = Assign(lhs, result) else: body = AugAssign(lhs, expr.op, result) body.set_fst(expr.fst) new_args.append([arg[0], [body]]) expr = If(*new_args) return self._annotate(expr, **settings) if isinstance(rhs, FunctionDef): # case of lambdify rhs = rhs.rename(_get_name(expr.lhs)) for i in rhs.body: i.set_fst(expr.fst) rhs = self._annotate(rhs, **settings) return rhs if isinstance(rhs, Block): results = _atomic(rhs.body,Return) sub = list(zip(results,[EmptyLine()]*len(results))) body = rhs.body body = subs(body,sub) results = [i.expr for i in results] lhs = expr.lhs if isinstance(lhs ,(list,tuple,Tuple)): sub = [list(zip(i,lhs)) for i in results] else: sub = [(i[0],lhs) for i in results] body = subs(body,sub) expr = Block(rhs.name, rhs.variables, body) return expr if isinstance(rhs, FunctionalFor): return rhs elif isinstance(rhs, CodeBlock): stmts = rhs.body stmt = stmts[-1] if isinstance(expr, Assign): stmt = Assign(expr.lhs, stmt) elif isinstance(expr, AugAssign): stmt = AugAssign(expr.lhs, expr.op, stmt) stmt.set_fst(expr.fst) stmt = self._annotate(stmt, **settings) stmts[-1] = stmt return CodeBlock(stmts) # d_var can be a list of dictionaries if isinstance(rhs, ConstructorCall): cls_name = rhs.func.cls_name # create a new Datatype for the current class cls = self.get_class(cls_name) dtype = self.get_class_construct(cls_name)() # to be moved to infere_type? d_var = {} d_var['datatype'] = dtype d_var['allocatable'] = False d_var['shape'] = () d_var['rank'] = 0 d_var['is_target'] = True # set target to True if we want the class objects to be pointers d_var['is_polymorphic'] = False d_var['cls_base'] = cls d_var['is_pointer'] = False elif isinstance(rhs, Application): # ARA: needed for functions defined only with a header name = _get_name(rhs) func = self.get_function(name) if isinstance(func, FunctionDef): results = func.results if results: d_var = [self._infere_type(i, **settings) for i in results] elif isinstance(func, Interface): d_var = [self._infere_type(i, **settings) for i in func.functions[0].results] # TODO imporve this will not work for the case of different results type d_var[0]['datatype'] = sp_dtype(rhs) else: d_var = self._infere_type(rhs, **settings) elif isinstance(rhs, SumFunction): d_var = self._infere_type(rhs.body, **settings) elif isinstance(rhs, Map): name = str(rhs.args[0]) func = self.get_function(name) if func is None: errors.report(UNDEFINED_FUNCTION, symbol=name, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) dvar = self._infere_type(rhs.args[1], **settings) d_var = [self._infere_type(result, **settings) for result in func.results] for i in range(len(d_var)): d_var[i]['shape'] = dvar['shape'] d_var[i]['rank'] = dvar['rank'] else: d_var = self._infere_type(rhs, **settings) if d_var['datatype' ].__class__.__name__.startswith('Pyccel'): d_var['cls_base'] = self.get_class(d_var['datatype' ].__class__.__name__[6:]) d_var['is_pointer'] = d_var['is_target'] \ or d_var['is_pointer'] # TODO if we want to use pointers then we set target to true # in the ConsturcterCall d_var['is_polymorphic'] = False if d_var['is_target']: if isinstance(rhs, Symbol): d_var['is_target'] = False d_var['is_pointer'] = True # case of rhs is a target variable the lhs must be a pointer lhs = expr.lhs if isinstance(lhs, Symbol): if isinstance(d_var, list): if len(d_var) > 1: raise ValueError('can not assign multiple object into one variable' ) elif len(d_var) == 1: d_var = d_var[0] name = _get_name(lhs) dtype = d_var.pop('datatype') lhs = Variable(dtype, name, **d_var) var = self.get_variable(name) if var is None: self.insert_variable(lhs, name=lhs.name) else: # TODO improve check type compatibility if str(lhs.dtype) != str(var.dtype): txt = \ '|{name}| {old} <-> {new}'.format(name=name, old=var.dtype, new=lhs.dtype) # case where the rhs is of native type # TODO add other native types if isinstance(rhs, (Integer, Float)): errors.report(INCOMPATIBLE_TYPES_IN_ASSIGNMENT, symbol=txt, bounding_box=self.bounding_box, severity='error', blocker=False) else: errors.report(INCOMPATIBLE_TYPES_IN_ASSIGNMENT, symbol=txt, bounding_box=self.bounding_box, severity='internal', blocker=False) elif isinstance(lhs, (IndexedVariable, IndexedBase)): # TODO check consistency of indices with shape/rank name = _get_name(lhs) var = self.get_variable(name) if var is None: # TODO ERROR not tested yet errors.report(UNDEFINED_VARIABLE, symbol=name, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) dtype = var.dtype prec = var.precision shape = var.shape order = var.order rank = var.rank lhs = IndexedVariable(name, dtype=dtype,shape=shape,prec=prec,order=order, rank=rank) elif isinstance(lhs, (IndexedElement, Indexed)): # TODO check consistency of indices with shape/rank name = _get_name(lhs) var = self.get_variable(name) if var is None: errors.report(UNDEFINED_INDEXED_VARIABLE, symbol=name, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) args = list(lhs.indices) if var.order == 'C': args.reverse() args = tuple(args) dtype = var.dtype prec = var.precision order = var.order rank = var.rank shape = var.shape lhs = IndexedVariable(name, dtype=dtype, shape=shape,prec=prec,order=order, rank=rank).__getitem__(*args) elif isinstance(lhs, DottedVariable): dtype = d_var.pop('datatype') name = lhs.lhs.name if self._current == '__init__': cls_name = str(self.get_variable('self' ).cls_base.name) cls = self.get_class(cls_name) attributes = cls.attributes parent = cls.parent attributes = list(attributes) n_name = str(lhs.rhs.name) attributes += [Variable(dtype, n_name, **d_var)] # update the attributes of the class and push it to the namespace self.insert_class(ClassDef(cls_name, attributes, [], parent=parent)) # update the self variable with the new attributes dt = self.get_class_construct(cls_name)() var = Variable(dt, 'self', cls_base=self.get_class(cls_name)) self.insert_variable(var, 'self') lhs = DottedVariable(var, Variable(dtype, n_name, **d_var)) else: lhs = self._annotate(lhs, **settings) if isinstance(rhs, (Map, Zip)): func = _get_name(rhs.args[0]) func = Function(func) alloc = Assign(lhs,Zeros(lhs.shape,lhs.dtype)) alloc.set_fst(expr.fst) index = self.create_variable(expr) index = Variable('int',index.name) range_ = Function('range')(Function('len')(lhs)) name = _get_name(lhs) var = IndexedBase(name)[index] args = rhs.args[1:] args = [_get_name(arg) for arg in args] args = [IndexedBase(arg)[index] for arg in args] body = [Assign(var,func(*args))] body[0].set_fst(expr.fst) body = For(index, range_, body, strict=False) body = self._annotate(body, **settings) body = [alloc , body] return CodeBlock(body) expr_new = Assign(lhs, rhs, strict=False) if not isinstance(lhs, (list, Tuple, tuple)): if isinstance(d_var,dict): d_var = [d_var] for (i, dic) in enumerate(d_var): if not isinstance(lhs, (list, Tuple, tuple)): lhs = [lhs] allocatable = False is_pointer = False if dic['allocatable']: allocatable = True if dic['is_pointer']: is_pointer = True if 'is_target' in dic.keys() and dic['is_target'] \ and isinstance(rhs, Variable): is_pointer = True if isinstance(expr_new.rhs, IndexedElement) \ and expr_new.lhs.rank > 0: allocatable = True elif isinstance(expr_new.rhs, Variable) \ and isinstance(expr_new.rhs.dtype, NativeList): is_pointer = True if isinstance(lhs, Variable) and (allocatable or is_pointer): lhs[i] = self.update_variable(expr_new.lhs[i], allocatable=allocatable, is_pointer=is_pointer) if len(lhs) == 1: lhs = lhs[0] is_pointer = is_pointer and isinstance(rhs, (Variable, Dlist,DottedVariable)) if is_pointer: expr_new = AliasAssign(lhs, rhs) elif expr_new.is_symbolic_alias: expr_new = SymbolicAssign(lhs, rhs) # in a symbolic assign, the rhs can be a lambda expression # it is then treated as a def node F = self.get_symbolic_function(lhs) if F is None: self.insert_symbolic_function(expr_new) else: raise NotImplementedError('TODO') if isinstance(expr, AugAssign): expr_new = AugAssign(expr_new.lhs, expr.op, expr_new.rhs) # we need to set the fst again in case # we annotate it again expr_new.set_fst(expr.fst) return expr_new elif isinstance(expr, For): # treatment of the index/indices iterable = self._annotate(expr.iterable, **settings) body = list(expr.body) iterator = expr.target if isinstance(iterable, Variable): indx = self.create_variable(iterable) assign = Assign(iterator, IndexedBase(iterable)[indx]) assign.set_fst(expr.fst) iterator = indx body = [assign] + body elif isinstance(iterable, Map): indx = self.create_variable(iterable) func = iterable.args[0] args = [IndexedBase(arg)[indx] for arg in iterable.args[1:]] assing = assign = Assign(iterator, func(*args)) assign.set_fst(expr.fst) iterator = indx body = [assign] + body elif isinstance(iterable, Zip): args = iterable.args indx = self.create_variable(args) for i in range(len(args)): assign = Assign(iterator[i], IndexedBase(args[i])[indx]) assign.set_fst(expr.fst) body = [assign] + body iterator = indx elif isinstance(iterable, Enumerate): indx = iterator.args[0] var = iterator.args[1] assign = Assign(var, IndexedBase(iterable.args[0])[indx]) assign.set_fst(expr.fst) iterator = indx body = [assign] + body elif isinstance(iterable, Product): args = iterable.args iterator = list(iterator) for i in range(len(args)): indx = self.create_variable(i) assign = Assign(iterator[i], IndexedBase(args[i])[indx]) assign.set_fst(expr.fst) body = [assign] + body iterator[i] = indx if isinstance(iterator, Symbol): name = iterator.name var = self.get_variable(name) target = var if var is None: target = Variable('int', name, rank=0) self.insert_variable(target) elif isinstance(iterator, list): target = [] for i in iterator: name = str(i.name) var = Variable('int', name, rank=0) self.insert_variable(var) target.append(var) else: dtype = type(iterator) # TODO ERROR not tested yet errors.report(INVALID_FOR_ITERABLE, symbol=expr.target, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) body = self._annotate(body, **settings) if isinstance(iterable, Variable): return ForIterator(target, iterable, body) return For(target, iterable, body) elif isinstance(expr, GC): target = expr.target lhs_name = _get_name(target.lhs) stmt = self.get_variable(lhs_name) if stmt is None: stmt = Assign(target.lhs, 0) stmt.set_fst(target.fst) else: stmt = True stmt = self._annotate(stmt, **settings) loops = [self._annotate(i, **settings) for i in expr.loops] target = self._annotate(target, **settings) if isinstance(target, CodeBlock): target = target.body[-1] elif isinstance(target, If): target = target.bodies[0] d_var = self._infere_type(target.rhs, **settings) dtype = d_var.pop('datatype') lhs = None if isinstance(target.lhs, Symbol): lhs = Variable(dtype, lhs_name, **d_var) self.insert_variable(lhs) if stmt: if isinstance(expr, FunctionalSum): val = 0 if str_dtype(dtype) in ['real', 'complex']: val = 0.0 elif isinstance(expr, FunctionalMin): val = INF elif isinstance(expr, FunctionalMax): val = -INF stmt = Assign(target.lhs, val) stmt.set_fst(expr.fst) loops.insert(0, stmt) if isinstance(expr, FunctionalSum): expr = FunctionalSum(loops, lhs, []) elif isinstance(expr, FunctionalMin): expr = FunctionalMin(loops, lhs, []) elif isinstance(expr, FunctionalMax): expr = FunctionalMax(loops, lhs, []) return expr elif isinstance(expr, FunctionalFor): target = expr.target index = expr.index indexes = expr.indexes dims = [] body = expr.loops[1] while isinstance(body, For): a = self._annotate(body.iterable, **settings) stop = None start = 0 step = 1 var = body.target if isinstance(a, Range): var = Variable('int', var.name) stop = a.stop start = a.start step = a.step elif isinstance(a, (Zip, Enumerate)): dvar = self._infere_type(a.element, **settings) dtype = dvar.pop('datatype') if dvar['rank'] > 0: dvar['rank'] -= 1 dvar['shape'] = (dvar['shape'])[1:] if dvar['rank'] == 0: dvar['allocatable'] = dvar['is_pointer'] = False var = Variable(dtype, var.name, **dvar) stop = a.element.shape[0] elif isinstance(a, Variable): dvar = self._infere_type(a, **settings) dtype = dvar.pop('datatype') if dvar['rank'] > 0: dvar['rank'] -= 1 dvar['shape'] = (dvar['shape'])[1:] if dvar['rank'] == 0: dvar['allocatable'] = dvar['is_pointer'] = False var = Variable(dtype, var.name, **dvar) stop = a.shape[0] else: raise NotImplementedError('TODO') self.insert_variable(var) size = (stop - start) / step dims.append((size, step, start, stop)) body = body.body[0] # we now calculate the size of the array which will be allocated for i in range(len(indexes)): var = self.get_variable(indexes[i].name) if var is None: raise ValueError('variable not found') indexes[i] = var dim = dims[-1][0] for i in range(len(dims) - 1, 0, -1): size = dims[i - 1][0] step = dims[i - 1][1] start = dims[i - 1][2] size = ceiling(size) dim = ceiling(dim) dim = dim.subs(indexes[i - 1], start + step * indexes[i - 1]) dim = Summation(dim, (indexes[i - 1], 0, size - 1)) dim = dim.doit() if isinstance(dim, Summation): raise NotImplementedError('TODO') # TODO find faster way to calculate dim when step>1 and not isinstance(dim, Sum) # maybe use the c++ library of sympy # we annotate the target.rhs to infere the type of the list created rhs_args = [] rhs = target.rhs while isinstance(rhs, IfTernaryOperator): rhs_args.append(rhs.args[0][1][0]) rhs = rhs.args[1][1][0] rhs_args.append(rhs) rhs = Add(*rhs_args) # we do this to infere types correctly rhs = self._annotate(rhs, **settings) lhs_name = _get_name(target.lhs) d_var = self._infere_type(rhs, **settings) dtype = d_var.pop('datatype') d_var['rank'] += 1 shape = list(d_var['shape']) d_var['is_pointer'] = True shape.append(dim) d_var['shape'] = Tuple(*shape) lhs = Variable(dtype, lhs_name, **d_var) self.insert_variable(lhs) loops = [self._annotate(i, **settings) for i in expr.loops] index = self._annotate(index, **settings) return FunctionalFor(loops, lhs, indexes, index) elif isinstance(expr, While): test = self._annotate(expr.test, **settings) body = self._annotate(expr.body, **settings) return While(test, body) elif isinstance(expr, If): args = self._annotate(expr.args, **settings) return If(*args) elif isinstance(expr, VariableHeader): # TODO improve # move it to the ast like create_definition for FunctionHeader? name = expr.name d_var = expr.dtypes dtype = d_var.pop('datatype') var = Variable(dtype, name, **d_var) self.insert_variable(var) return expr elif isinstance(expr, FunctionHeader): # TODO should we return it and keep it in the AST? self.insert_header(expr) return expr elif isinstance(expr, ClassHeader): # TODO should we return it and keep it in the AST? self.insert_header(expr) return expr elif isinstance(expr, InterfaceHeader): container = self.namespace['functions'] # TODO improve test all possible containers if set(expr.funcs).issubset(container.keys()): name = expr.name funcs = [] for i in expr.funcs: funcs += [container[i]] expr = Interface(name, funcs, hide=True) container[name] = expr return expr elif isinstance(expr, Return): results = expr.expr new_vars = [] assigns = [] if not isinstance(results, (list, Tuple, List)): results = [results] for result in results: if isinstance(result, Expr) and not isinstance(result, Symbol): new_vars += [self.create_variable(result)] stmt = Assign(new_vars[-1], result) stmt.set_fst(expr.fst) assigns += [stmt] assigns[-1].set_fst(expr.fst) if len(assigns) == 0: results = [self._annotate(result, **settings) for result in results] return Return(results) else: assigns = [self._annotate(assign, **settings) for assign in assigns] new_vars = [self._annotate(i, **settings) for i in new_vars] assigns = CodeBlock(assigns) return Return(new_vars, assigns) elif isinstance(expr, FunctionDef): name = str(expr.name) name = name.replace("'", '') # remove quotes for str representation cls_name = expr.cls_name hide = False kind = 'function' decorators = expr.decorators funcs = [] is_static = False header = expr.header if header is None: if cls_name: header = self.get_header(cls_name + """.""" + name) else: header = self.get_header(name) if expr.arguments and not header: # TODO ERROR wrong position errors.report(FUNCTION_TYPE_EXPECTED, symbol=name, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) # we construct a FunctionDef from its header if header: interfaces = header.create_definition() # is_static will be used for f2py is_static = header.is_static # get function kind from the header kind = header.kind else: # this for the case of a function without arguments => no header interfaces = [FunctionDef(name, [], [], [])] vec_func = None if 'vectorize' in decorators: vec_name = 'vec_' + name arg = decorators['vectorize'][0] arg = str(arg.name) args = [str(i.name) for i in expr.arguments] index_arg = args.index(arg) arg = Symbol(arg) vec_arg = IndexedBase(arg) index = self.create_variable(expr.body) range_ = Function('range')(Function('len')(arg)) args = symbols(args) args[index_arg] = vec_arg[index] body_vec = Assign(args[index_arg],Function(name)(*args)) body_vec.set_fst(expr.fst) body_vec = [For(index,range_,[body_vec],strict=False)] header_vec = header.vectorize(index_arg) vec_func = expr.vectorize(body_vec, header_vec) for m in interfaces: args = [] results = [] local_vars = [] global_vars = [] imports = [] arg = None self.set_current_fun(name) arguments = expr.arguments if cls_name and str(arguments[0].name) == 'self': arg = arguments[0] arguments = arguments[1:] dt = self.get_class_construct(cls_name)() var = Variable(dt, 'self', cls_base=self.get_class(cls_name)) self.insert_variable(var, 'self') if arguments: for (a, ah) in zip(arguments, m.arguments): d_var = self._infere_type(ah, **settings) dtype = d_var.pop('datatype') # this is needed for the static case additional_args = [] if isinstance(a, ValuedArgument): # optional argument only if the value is None if isinstance(a.value, Nil): d_var['is_optional'] = True a_new = ValuedVariable(dtype, str(a.name), value=a.value, **d_var) else: # add shape as arguments if is_static and arg is array rank = d_var['rank'] if is_static and rank > 0: for i in range(0, rank): n_name = 'n{i}_{name}'.format( name=str(a.name), i=i) n_arg = Variable('int', n_name) # TODO clean namespace later var = self.get_variable(n_name) if not var is None: # TODO ERROR not tested yet errors.report(REDEFINING_VARIABLE, symbol=n_name, severity='error', blocker=self.blocking) self.insert_variable(n_arg) additional_args += [n_arg] # update shape # TODO can this be improved? add some check d_var['shape'] = Tuple(*additional_args) a_new = Variable(dtype, str(a.name), **d_var) if additional_args: args += additional_args args.append(a_new) self.insert_variable(a_new, name=str(a_new.name)) if len(interfaces) == 1 and len(interfaces[0].results) == 1: # case of recursive function # TODO improve self.insert_function(interfaces[0]) # we annotate the body body = [self._annotate(i, **settings) for i in expr.body] # find return stmt and results returns = self._collect_returns_stmt(body) results = [] for stmt in returns: results += [set(stmt.expr)] if not all(i == results[0] for i in results): raise PyccelSemanticError('multiple returns with different variables not available yet' ) if len(results) > 0: results = list(results[0]) if arg and cls_name: dt = self.get_class_construct(cls_name)() var = Variable(dt, 'self', cls_base=self.get_class(cls_name)) args = [var] + args for var in self.get_variables(): if not var in args + results: local_vars += [var] # TODO should we add all the variables or only the ones used in the function for var in self.get_variables('parent'): if not var in args + results + local_vars: global_vars += [var] is_recursive = False # get the imports imports = self._scope[self._current]['imports'] imports = list(set(imports)) self.set_current_fun(None) func_ = self.get_function(name) if not func_ is None and func_.is_recursive: is_recursive = True func = FunctionDef( name, args, results, body, local_vars=local_vars, global_vars=global_vars, cls_name=cls_name, hide=hide, kind=kind, is_static=is_static, imports=imports, decorators=decorators, is_recursive=is_recursive, ) if cls_name: cls = self.get_class(cls_name) methods = list(cls.methods) + [func] # update the class methods self.insert_class(ClassDef(cls_name, cls.attributes, methods, parent=cls.parent)) funcs += [func] #clear the sympy cache #TODO move that inside FunctionDef #and clear all variable except the global one cache.clear_cache() if len(funcs) == 1: funcs = funcs[0] self.insert_function(funcs) else: funcs = [f.rename(name + '_' + str(i)) for (i, f) in enumerate(funcs)] funcs = Interface(name, funcs) self.insert_function(funcs) if vec_func: vec_func = self._annotate(vec_func, **settings) if isinstance(funcs, Interface): funcs = list(funcs.funcs)+[vec_func] else: funcs = funcs.rename('sc_'+ name) funcs = [funcs,vec_func] funcs = Interface(name, funcs) self.insert_function(funcs) return funcs elif isinstance(expr, Print): args = self._annotate(expr.expr, **settings) if len(args) == 0: raise ValueError('no arguments given to print function') is_symbolic = lambda var: isinstance(var, Variable) \ and isinstance(var.dtype, NativeSymbol) test = all(is_symbolic(i) for i in args) # TODO fix: not yet working because of mpi examples # if not test: # raise ValueError('all arguments must be either symbolic or none of them') if is_symbolic(args[0]): _args = [] for a in args: f = self.get_symbolic_function(a.name) if f is None: _args.append(a) else: # TODO improve: how can we print SymbolicAssign as lhs = rhs _args.append(f) return SymbolicPrint(_args) else: return Print(args) elif isinstance(expr, ClassDef): # TODO - improve the use and def of interfaces # - wouldn't be better if it is done inside ClassDef? name = str(expr.name) name = name.replace("'", '') methods = list(expr.methods) parent = expr.parent interfaces = [] # remove quotes for str representation self.insert_class(ClassDef(name, [], [], parent=parent)) const = None for (i, method) in enumerate(methods): m_name = str(method.name).replace("'", '') # remove quotes for str representation if m_name == '__init__': const = self._annotate(method) methods.pop(i) methods = [self._annotate(i) for i in methods] if not const: errors.report(UNDEFINED_INIT_METHOD, symbol=name, bounding_box=self.bounding_box, severity='error', blocker=True) methods = [const] + methods header = self.get_header(name) if not header: raise ValueError('Expecting a header class for {classe} but could not find it.'.format(classe=name)) options = header.options attributes = self.get_class(name).attributes for i in methods: if isinstance(i, Interface): methods.remove(i) interfaces += [i] return ClassDef(name, attributes, methods, interfaces=interfaces, parent=parent) elif isinstance(expr, Del): ls = self._annotate(expr.variables) return Del(ls) elif isinstance(expr, Is): # TODO ERROR wrong position if not isinstance(expr.rhs, Nil): errors.report(PYCCEL_RESTRICTION_IS_RHS, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) name = expr.lhs var = self.get_variable(str(name)) if var is None: errors.report(UNDEFINED_VARIABLE, symbol=name, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) return Is(var, expr.rhs) elif isinstance(expr, Import): # TODO - must have a dict where to store things that have been # imported # - should not use namespace if expr.source: container = self._imports if self._current: container = container[self._current] if str(expr.source) in pyccel_builtin_import_registery: imports = pyccel_builtin_import(expr) for (name, atom) in imports: if not name is None: F = self.get_variable(name) if F is None: container[name] = atom elif name in container: errors.report(FOUND_DUPLICATED_IMPORT, symbol=name, severity='warning') else: raise NotImplementedError('must report error') else: # in some cases (blas, lapack, openmp and openacc level-0) # the import should not appear in the final file # all metavars here, will have a prefix and suffix = __ __ignore_at_import__ = False __module_name__ = None __import_all__ = False # we need to use str here since source has been defined # using repr. # TODO shall we improve it? targets = [_get_name(i) for i in expr.target] p = self.d_parsers[str(expr.source)] for entry in ['variables', 'classes', 'functions', 'cls_constructs']: d_self = self._namespace[entry] d_son = p.namespace[entry] for (k, v) in list(d_son.items()): # TODO test if it is not already in the namespace if k in targets: d_self[k] = v # we add all the macros from the son self._namespace['macros' ].update(p.namespace['macros']) # ... meta variables if 'ignore_at_import' in list(p.metavars.keys()): __ignore_at_import__ = \ p.metavars['ignore_at_import'] if 'import_all' in list(p.metavars.keys()): __import_all__ = p.metavars['import_all'] if 'module_name' in list(p.metavars.keys()): __module_name__ = p.metavars['module_name'] expr = Import(expr.target, __module_name__) # ... if 'print' in p.metavars.keys(): source = str(expr.source).split('.')[-1] source = 'mod_' + source expr = Import(expr.target,source=source) if not __ignore_at_import__: return expr else: if __import_all__: expr = Import(__module_name__) self.insert_import(expr) # we return the expr when we are in # program if self._current is None: return expr return EmptyLine() return expr elif isinstance(expr, With): domaine = self._annotate(expr.test) parent = domaine.cls_base if not parent.is_with_construct: raise ValueError('with construct can only applied to classes with __enter__ and __exit__ methods' ) body = self._annotate(expr.body) return With(domaine, body, None).block elif isinstance(expr, MacroFunction): # we change here the master name to its FunctionDef f_name = expr.master header = self.get_header(f_name) if header is None: func = self.get_function(f_name) if func is None: errors.report(MACRO_MISSING_HEADER_OR_FUNC, symbol=f_name, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) else: interfaces = header.create_definition() # TODO -> Said: must handle interface func = interfaces[0] name = expr.name args = expr.arguments master_args = expr.master_arguments results = expr.results macro = MacroFunction(name, args, func, master_args, results=results) self.insert_macro(macro) return macro elif isinstance(expr, MacroVariable): master = expr.master if isinstance(master, DottedName): raise NotImplemented('TODO') header = self.get_header(master) if header is None: var = self.get_variable(master) if var is None: errors.report(MACRO_MISSING_HEADER_OR_FUNC, symbol=master, bounding_box=self.bounding_box, severity='error', blocker=self.blocking) else: var = Variable(header.dtype, header.name) # TODO -> Said: must handle interface expr = MacroVariable(expr.name, var) self.insert_macro(expr) return expr elif isinstance(expr, Dlist): val = self._annotate(expr.val, **settings) if isinstance(val, (Tuple, list, tuple)): raise PyccelSemanticError('list initialisation of dimesion > 1 not yet supported' ) shape = self._annotate(expr.length, **settings) return Dlist(val, shape) else: raise PyccelSemanticError('{expr} not yet available'.format(expr=type(expr)))
[docs]class PyccelParser(Parser): pass
###################################################### if __name__ == '__main__': import sys try: filename = sys.argv[1] except: raise ValueError('Expecting an argument for filename') pyccel = Parser(filename) pyccel.parse(verbose=True) settings = {} pyccel.annotate(**settings) # for s in pyccel.ast: # print(type(s)) # # ... using Pickle # # export the ast # pyccel.dump() # # # load the ast # pyccel.load() # # ... # pyccel.view_namespace('variables') # pyccel.print_namespace() # pyccel.dot('ast.gv')