Source code for pyccel.ast.headers
# coding: utf-8
# TODO must use Header.__new__ rather than Basic.__new__
from sympy.utilities.iterables import iterable
from sympy.core import Symbol
from sympy import sympify, Tuple
from .core import Basic
from .core import Variable
from .core import ValuedArgument
from .core import FunctionDef, Interface
from .core import ClassDef
from .core import DottedName, DottedVariable
from .datatypes import datatype, DataTypeFactory, UnionType
from .macros import Macro, MacroShape, construct_macro
from .core import local_sympify
[docs]class MetaVariable(Header):
"""Represents the MetaVariable."""
def __new__(cls, name, value):
if not isinstance(name, str):
raise TypeError('name must be of type str')
# TODO check value
return Basic.__new__(cls, name, value)
@property
def name(self):
return self._args[0]
@property
def value(self):
return self._args[1]
# TODO rename dtypes to arguments
[docs]class VariableHeader(Header):
"""Represents a variable header in the code.
name: str
variable name
dtypes: dict
a dictionary for typing
Examples
"""
# TODO dtypes should be a dictionary (useful in syntax)
def __new__(cls, name, dtypes):
if not(isinstance(dtypes, dict)):
raise TypeError("Expecting dtypes to be a dict.")
return Basic.__new__(cls, name, dtypes)
@property
def name(self):
return self._args[0]
@property
def dtypes(self):
return self._args[1]
# TODO rename dtypes to arguments
def __getnewargs__(self):
"""used for Pickling self."""
# TODO improve after renaming the args property
args = (self._args[0],)
return args
[docs]class FunctionHeader(Header):
"""Represents function/subroutine header in the code.
func: str
function/subroutine name
dtypes: tuple/list
a list of datatypes. an element of this list can be str/DataType of a
tuple (str/DataType, attr, allocatable)
results: tuple/list
a list of datatypes. an element of this list can be str/DataType of a
tuple (str/DataType, attr, allocatable)
kind: str
'function' or 'procedure'. default value: 'function'
is_static: bool
True if we want to pass arrays in f2py mode. every argument of type
array will be preceeded by its shape, the later will appear in the
argument declaration. default value: False
Examples
>>> from pyccel.ast.core import FunctionHeader
>>> FunctionHeader('f', ['double'])
FunctionHeader(f, [(NativeDouble(), [])])
"""
# TODO dtypes should be a dictionary (useful in syntax)
def __new__(cls, func, dtypes,
results=None,
kind='function',
is_static=False):
func = str(func)
if not(iterable(dtypes)):
raise TypeError("Expecting dtypes to be iterable.")
if results:
if not(iterable(results)):
raise TypeError("Expecting results to be iterable.")
if not isinstance(kind, str):
raise TypeError("Expecting a string for kind.")
if not (kind in ['function', 'procedure']):
raise ValueError("kind must be one among {'function', 'procedure'}")
if not isinstance(is_static, bool):
raise TypeError('is_static must be a boolean')
return Basic.__new__(cls, func, dtypes, results, kind, is_static)
@property
def func(self):
return self._args[0]
@property
def dtypes(self):
return self._args[1]
@property
def results(self):
return self._args[2]
@property
def kind(self):
return self._args[3]
@property
def is_static(self):
return self._args[4]
[docs] def create_definition(self):
"""Returns a FunctionDef with empy body."""
# TODO factorize what can be factorized
from itertools import product
name = str(self.func)
body = []
cls_name = None
hide = False
kind = self.kind
is_static = self.is_static
imports = []
funcs = []
dtypes = []
for i in self.dtypes:
if isinstance(i, UnionType):
dtypes += [i.args]
elif isinstance(i, dict):
dtypes += [[i]]
else:
raise TypeError('element must be of type UnionType or dict')
for args_ in product(*dtypes):
args = []
for i, d in enumerate(args_):
dtype = d['datatype']
allocatable = d['allocatable']
is_pointer = d['is_pointer']
precision = d['precision']
rank = d['rank']
if rank>0 and allocatable:#case of ndarray
if dtype in ['int', 'double', 'float', 'complex']:
allocatable = True
dtype = 'ndarray'+dtype
order = None
if rank >1:
order = d['order']
shape = None
if isinstance(dtype, str):
try:
dtype = datatype(dtype)
except:
#TODO check if it's a class type before
if isinstance(dtype, str):
dtype = DataTypeFactory(str(dtype), ("_name"))()
is_pointer = True
arg_name = 'arg_{0}'.format(str(i))
arg = Variable(dtype, arg_name,
allocatable=allocatable, is_pointer=is_pointer,
rank=rank, shape=shape ,order = order, precision = precision)
args.append(arg)
# ... factorize the following 2 blocks
results = []
for i,d_var in enumerate(self.results):
dtype = d_var.pop('datatype')
var = Variable(dtype, 'res_{}'.format(i), **d_var)
results.append(var)
# we put back dtype otherwise macro will crash when it tries to
# call create_definition
d_var['datatype'] = dtype
func= FunctionDef(name, args, results, body,
local_vars=[],
global_vars=[],
cls_name=cls_name,
hide=hide,
kind=kind,
is_static=is_static,
imports=imports)
funcs += [func]
return funcs
[docs] def to_static(self):
"""returns a static function header. needed for f2py"""
return FunctionHeader(self.func,
self.dtypes,
self.results,
self.kind,
True)
[docs] def vectorize(self,index):
""" add a dimension to one of the arguments specified by it's position"""
types = self.dtypes
types[index]['rank'] += 1
types[index]['allocatable'] = True
return FunctionHeader(self.func,
types,
self.results,
self.kind,
self.is_static)
def __getnewargs__(self):
"""used for Pickling self."""
args = (self.func,
self.dtypes,
self.results,
self.kind,
self.is_static,)
return args
# TODO to be improved => use FunctionHeader
[docs]class MethodHeader(FunctionHeader):
"""Represents method header in the code.
name: iterable
method name as a list/tuple
dtypes: tuple/list
a list of datatypes. an element of this list can be str/DataType of a
tuple (str/DataType, attr)
results: tuple/list
a list of datatypes. an element of this list can be str/DataType of a
tuple (str/DataType, attr)
kind: str
'function' or 'procedure'. default value: 'function'
is_static: bool
True if we want to pass arrays in f2py mode. every argument of type
array will be preceeded by its shape, the later will appear in the
argument declaration. default value: False
Examples
>>> from pyccel.ast.core import MethodHeader
>>> m = MethodHeader(('point', 'rotate'), ['double'])
>>> m
MethodHeader((point, rotate), [(NativeDouble(), [])], [])
>>> m.name
'point.rotate'
"""
def __new__(cls, name, dtypes, results=None, kind='function', is_static=False):
if not isinstance(name, (list, tuple)):
raise TypeError("Expecting a list/tuple of strings.")
if not(iterable(dtypes)):
raise TypeError("Expecting dtypes to be iterable.")
for d in dtypes:
if not isinstance(d, UnionType) and not isinstance(d, dict):
raise TypeError("Wrong element in dtypes.")
for d in results:
if not isinstance(d, UnionType):
raise TypeError("Wrong element in dtypes.")
if not isinstance(kind, str):
raise TypeError("Expecting a string for kind.")
if not (kind in ['function', 'procedure']):
raise ValueError("kind must be one among {'function', 'procedure'}")
if not isinstance(is_static, bool):
raise TypeError('is_static must be a boolean')
return Basic.__new__(cls, name, dtypes, results, kind, is_static)
@property
def name(self):
_name = self._args[0]
if isinstance(_name, str):
return _name
else:
return '.'.join(str(n) for n in _name)
@property
def dtypes(self):
return self._args[1]
@property
def results(self):
return self._args[2]
@property
def kind(self):
return self._args[3]
@property
def is_static(self):
return self._args[4]
[docs]class ClassHeader(Header):
"""Represents class header in the code.
name: str
class name
options: str, list, tuple
a list of options
Examples
>>> from pyccel.ast.core import ClassHeader
>>> ClassHeader('Matrix', ('abstract', 'public'))
ClassHeader(Matrix, (abstract, public))
"""
def __new__(cls, name, options):
if not(iterable(options)):
raise TypeError("Expecting options to be iterable.")
return Basic.__new__(cls, name, options)
@property
def name(self):
return self._args[0]
@property
def options(self):
return self._args[1]
# TODO must extend Header rather than Basic
[docs]class InterfaceHeader(Basic):
def __new__(cls, name, funcs):
if not isinstance(name,str):
raise TypeError('name should of type str')
if not all([isinstance(i, str) for i in funcs]):
raise TypeError('functions name must be of type str')
return Basic.__new__(cls, name, funcs)
@property
def name(self):
return self._args[0]
@property
def funcs(self):
return self._args[1]
[docs]class MacroFunction(Header):
"""."""
def __new__(cls, name, args, master, master_args, results=None):
if not isinstance(name, (str, Symbol)):
raise TypeError('name must be of type str or Symbol')
# master can be a string or FunctionDef
if not isinstance(master, (str, FunctionDef, Interface)):
raise ValueError('Expecting a master name of FunctionDef')
# we sympify everything since a macro is operating on symbols
if not(args is None):
args = [sympify(a, locals=local_sympify) for a in args]
if not(master_args is None):
master_args = [sympify(a, locals=local_sympify) for a in master_args]
if not(results is None):
results = [sympify(a, locals=local_sympify) for a in results]
return Basic.__new__(cls, name, args, master, master_args, results)
@property
def name(self):
return self._args[0]
@property
def arguments(self):
return self._args[1]
@property
def master(self):
return self._args[2]
@property
def master_arguments(self):
return self._args[3]
@property
def results(self):
return self._args[4]
# TODO: must be moved to annotation, once we add AliasVariables
# this is needed if we have to create a pointer or allocate a new
# variable to store the result
[docs] def apply(self, args, results=None):
"""returns the appropriate arguments."""
d_arguments = {}
if len(args) > 0:
sorted_args = []
unsorted_args = []
j = -1
for ind, i in enumerate(args):
if not isinstance(i, ValuedArgument):
sorted_args.append(i)
else:
j=ind
break
if j>0:
unsorted_args = args[j:]
for i in unsorted_args:
if not isinstance(i, ValuedArgument):
raise ValueError('variable not allowed after an optional argument')
for i in self.arguments[len(sorted_args):]:
if not isinstance(i, ValuedArgument):
raise ValueError('variable not allowed after an optional argument')
for arg,val in zip(self.arguments[:len(sorted_args)],sorted_args):
if not isinstance(arg, Tuple):
d_arguments[arg.name] = val
else:
if not isinstance(val, (list, Tuple,tuple)):
val = [val]
#TODO improve add more checks and generalize
if len(val)>len(arg):
raise ValueError('length mismatch of argument and its value ')
elif len(val)<len(arg):
for val_ in arg[len(val):]:
if isinstance(val_, ValuedArgument):
val +=Tuple(val_.value,)
else:
val +=Tuple(val_)
for arg_,val_ in zip(arg,val):
d_arguments[arg_.name] = val_
d_unsorted_args = {}
for arg in self.arguments[len(sorted_args):]:
d_unsorted_args[arg.name] = arg.value
for arg in unsorted_args:
if arg.name in d_unsorted_args.keys():
d_unsorted_args[arg.name] = arg.value
else:
raise ValueError('Unknown valued argument')
d_arguments.update(d_unsorted_args)
for i in d_arguments.keys():
arg = d_arguments[i]
if isinstance(arg, Macro):
d_arguments[i] = construct_macro(arg.name,
d_arguments[arg.argument.name])
if isinstance(arg, MacroShape):
d_arguments[i]._index = arg.index
d_results = {}
if not(results is None) and not(self.results is None):
for (r_macro, r) in zip(self.results, results):
# TODO improve name for other Nodes
d_results[r_macro.name] = r
# ...
# ... initialize new args with None
newargs = [None]*len(self.master_arguments)
# ...
argument_keys = d_arguments.keys()
result_keys = d_results.keys()
for i,arg in enumerate(self.master_arguments):
if isinstance(arg, Symbol):
if arg.name in argument_keys:
new = d_arguments[arg.name]
if isinstance(new, Symbol) and new.name in result_keys:
new = d_results[new.name]
elif arg.name in result_keys:
new = d_results[arg.name]
else:
new = arg
#TODO uncomment later
# raise ValueError('Unknown variable name')
elif isinstance(arg, Macro):
if arg.argument.name in argument_keys:
new = d_arguments[arg.argument.name]
if isinstance(new, Symbol) and new.name in result_keys:
new = d_results[new.name]
elif arg.argument.name in result_keys:
new = d_results[arg.argument.name]
else:
raise ValueError('Unkonwn variable name')
new = construct_macro(arg.name, new)
if isinstance(arg, MacroShape):
new._index = arg.index
newargs[i] = new
return newargs
[docs]class MacroVariable(Header):
"""."""
def __new__(cls, name, master):
if not isinstance(name, (str, Symbol, DottedName)):
raise TypeError('name must be of type str or DottedName')
if not isinstance(master, (str, Variable, DottedVariable)):
raise ValueError('Expecting a master name of Variable')
return Basic.__new__(cls, name, master)
@property
def name(self):
return self._args[0]
@property
def master(self):
return self._args[1]