Skip to content

Commit 9464954

Browse files
EmilyBourneyguclu
andauthored
Remove pyccel.ast.utilities.builtin_function (pyccel#1841)
The function `pyccel.ast.utilities.builtin_function` does the same thing as the code here: https://github.com/pyccel/pyccel/blob/78d56dec8303d728bc9cf43c55464fc42786645f/pyccel/parser/semantic.py#L1064-L1085 however it was not wrapped in a try/except and its implementation leaves no room for specialised `_visit` functions if the class needs more context information. This PR removes the function and uses the existing implementation to retrieve the same behaviour. In order to be able to wrap all builtin classes in a `PyccelFunctionDef` the inheritance is changed so `PythonRange`, `PythonMap`, `PythonEnumerate` and `PythonType` all inherit from `PyccelFunction`. As these objects do not represent objects in memory in the low level code the class type `SymbolicType()` is used. `PyccelInternalFunction` is renamed to `PyccelFunction` --------- Co-authored-by: Yaman Güçlü <[email protected]>
1 parent 2728d40 commit 9464954

File tree

19 files changed

+137
-135
lines changed

19 files changed

+137
-135
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ All notable changes to this project will be documented in this file.
5858
- \[INTERNALS\] Ensure `SemanticParser.infer_type` returns all documented information.
5959
- \[INTERNALS\] Enforce correct value for `pyccel_staging` property of `PyccelAstNode`.
6060
- \[INTERNALS\] Allow visiting objects containing both syntactic and semantic elements in `SemanticParser`.
61+
- \[INTERNALS\] Rename `pyccel.ast.internals.PyccelInternalFunction` to `pyccel.ast.internals.PyccelFunction`.
62+
- \[INTERNALS\] All internal classes which can be generated from `FunctionCall`s must inherit from `PyccelFunction`.
63+
- \[INTERNALS\] `PyccelFunction` objects which do not represent objects in memory have the type `SymbolicType`.
6164

6265
### Deprecated
6366

@@ -69,6 +72,7 @@ All notable changes to this project will be documented in this file.
6972
- \[INTERNALS\] Remove `ast.basic.TypedAstNode._dtype`. The datatype can still be accessed as it is contained within the class type.
7073
- \[INTERNALS\] Removed unused and undocumented function `get_function_from_ast`.
7174
- \[INTERNALS\] Remove unused parameters `expr`, `status` and `like` from `pyccel.ast.core.Assign`.
75+
- \[INTERNALS\] Remove `pyccel.ast.utilities.builtin_functions`.
7276

7377
## \[1.11.2\] - 2024-03-05
7478

developer_docs/ast_nodes.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ While translating from Python to the target language, Pyccel needs to store all
44

55
All objects in the Abstract Syntax Tree inherit from the class `pyccel.ast.basic.PyccelAstNode`. This class serves 2 roles. Firstly it provides a super class from which all our AST nodes can inherit which makes them easy to identify. Secondly it provides functionalities common to all AST nodes. For example it provides the `ast` property which allows the original code parsed by Python's `ast` module to be stored in the class. This object is important in order to report neat errors for code that cannot be handled by Pyccel. It also contains functions involving the relations between the nodes. These are explained in the section [Constructing a tree](#Constructing-a-tree).
66

7-
The inheritance tree for a Python AST node is often more complicated than directly inheriting from `PyccelAstNode`. In particular there are two classes which you will see in the inheritance throughout the code. These classes are `TypedAstNode` and `PyccelInternalFunction`. These classes are explained in more detail below.
7+
The inheritance tree for a Python AST node is often more complicated than directly inheriting from `PyccelAstNode`. In particular there are two classes which you will see in the inheritance throughout the code. These classes are `TypedAstNode` and `PyccelFunction`. These classes are explained in more detail below.
88

99
## Typed AST Node
1010

@@ -56,11 +56,11 @@ The static type is the class type that would be assigned to an object created us
5656

5757
## Pyccel Internal Function
5858

59-
The class `pyccel.ast.internals.PyccelInternalFunction` is a super class. This class should never be used directly but provides functionalities which are common to certain AST objects. These AST nodes are those which describe functions which are supported by Pyccel. For example it is used for functions from the `math` library, the `cmath` library, the `numpy` library, etc. `PyccelInternalFunction` inherits from `TypedAstNode`. The type information for the sub-class describes the type of the result of the function.
59+
The class `pyccel.ast.internals.PyccelFunction` is a super class. This class should never be used directly but provides functionalities which are common to certain AST objects. These AST nodes are those which describe functions which are supported by Pyccel. For example it is used for functions from the `math` library, the `cmath` library, the `numpy` library, etc. `PyccelFunction` inherits from `TypedAstNode`. The type information for the sub-class describes the type of the result of the function.
6060

61-
Instances of the subclasses of `PyccelInternalFunction` are created from a `FunctionCall` object resulting from the syntactic stage. The `PyccelInternalFunction` objects are initialised by passing the contents of the `FunctionCallArgument`s used in the `FunctionCall` to the constructor. The actual arguments may be either positional or keyword arguments, but the constructor of `PyccelInternalFunction` only accepts positional arguments through the variadic `*args`. It is therefore important that any subclasses of `PyccelInternalFunction` provide their own `__init__` with the correct function signature. I.e. a constructor whose positional and keyword arguments match the names of the positional and keyword arguments of the Python function being described.
61+
Instances of the subclasses of `PyccelFunction` are created from a `FunctionCall` object resulting from the syntactic stage. The `PyccelFunction` objects are initialised by passing the contents of the `FunctionCallArgument`s used in the `FunctionCall` to the constructor. The actual arguments may be either positional or keyword arguments, but the constructor of `PyccelFunction` only accepts positional arguments through the variadic `*args`. It is therefore important that any subclasses of `PyccelFunction` provide their own `__init__` with the correct function signature. I.e. a constructor whose positional and keyword arguments match the names of the positional and keyword arguments of the Python function being described.
6262

63-
The constructor of `PyccelInternalFunction` takes a tuple of arguments passed to the function. The arguments can be passed through in the constructor of the sub-class to let `PyccelInternalFunction` take care of saving the arguments, or the arguments can be saved with more useful names inside the class. However care must be taken to ensure that the argument is not saved inside the class twice. Additionally if the arguments are saved inside the sub-class then the `_attribute_nodes` static class attribute must be correctly set to ensure the tree is correctly constructed (see below). As `PyccelInternalFunction` implements `_attribute_nodes` these classes are some of the only ones which will not raise an error during the pull request tests if this information is missing.
63+
The constructor of `PyccelFunction` takes a tuple of arguments passed to the function. The arguments can be passed through in the constructor of the sub-class to let `PyccelFunction` take care of saving the arguments, or the arguments can be saved with more useful names inside the class. However care must be taken to ensure that the argument is not saved inside the class twice. Additionally if the arguments are saved inside the sub-class then the `_attribute_nodes` static class attribute must be correctly set to ensure the tree is correctly constructed (see below). As `PyccelFunction` implements `_attribute_nodes` these classes are some of the only ones which will not raise an error during the pull request tests if this information is missing.
6464

6565
## Constructing a tree
6666

developer_docs/how_to_solve_an_issue.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This file summarises basic approaches which should allow you to attempt some of
77
To add a new function:
88

99
- Determine the functions in C/Fortran that are equivalent to the function you want to support (ideally aim to support as many arguments as seems feasible, check the Python standard for the latest description of the function)
10-
- Add a class to represent the function. The class should go in the appropriate file in the [ast](../pyccel/ast) folder. This function will probably inherit from [PyccelInternalFunction](../pyccel/ast/internals.py)
10+
- Add a class to represent the function. The class should go in the appropriate file in the [ast](../pyccel/ast) folder. This function will probably inherit from [PyccelFunction](../pyccel/ast/internals.py)
1111
- Ensure the function is recognised in the semantic stage by adding it to the appropriate dictionary (see the function `builtin_function` and the dictionary `builtin_import_registery` in [ast/utilities.py](../pyccel/ast/utilities.py)
1212
- Add the print functions for the 3 languages
1313
- Add tests in the folder `tests/epyccel`

developer_docs/overview.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,20 @@ In the syntactic, semantic, and code generation stages a similar strategy is use
5353
#### Example
5454
Suppose we want to generate the code for an object of the class `NumpyTanh`, first we collect the inheritance tree of `NumpyTanh`. This gives us:
5555
```python
56-
('NumpyTanh', 'NumpyUfuncUnary', 'NumpyUfuncBase', 'PyccelInternalFunction', 'TypedAstNode', 'PyccelAstNode')
56+
('NumpyTanh', 'NumpyUfuncUnary', 'NumpyUfuncBase', 'PyccelFunction', 'TypedAstNode', 'PyccelAstNode')
5757
```
5858
Therefore the print functions which are acceptable for visiting this object are:
5959

6060
- `_print_NumpyTanh`
6161
- `_print_NumpyUfuncUnary`
6262
- `_print_NumpyUfuncBase`
63-
- `_print_PyccelInternalFunction`
63+
- `_print_PyccelFunction`
6464
- `_print_TypedAstNode`
6565
- `_print_PyccelAstNode`
6666

6767
We run through these possible functions choosing the one which is the most specialised. If none of these methods exist, then an error is raised.
6868

69-
In the case of `NumpyTanh` the function which will be selected is `_print_NumpyUfuncBase` when translating to C or Fortran, and `_print_PyccelInternalFunction` when translating to Python.
69+
In the case of `NumpyTanh` the function which will be selected is `_print_NumpyUfuncBase` when translating to C or Fortran, and `_print_PyccelFunction` when translating to Python.
7070

7171
### AST
7272

pyccel/ast/builtin_methods/list_methods.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"""
1212

1313
from pyccel.ast.datatypes import VoidType
14-
from pyccel.ast.internals import PyccelInternalFunction
14+
from pyccel.ast.internals import PyccelFunction
1515

1616
__all__ = ('ListAppend',
1717
'ListClear',
@@ -25,7 +25,7 @@
2525
)
2626

2727
#==============================================================================
28-
class ListMethod(PyccelInternalFunction):
28+
class ListMethod(PyccelFunction):
2929
"""
3030
Abstract class for list method calls.
3131

pyccel/ast/builtin_methods/set_methods.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
This module contains objects which describe these methods within Pyccel's AST.
1111
"""
1212
from pyccel.ast.datatypes import VoidType
13-
from pyccel.ast.internals import PyccelInternalFunction
13+
from pyccel.ast.internals import PyccelFunction
1414
from pyccel.ast.basic import TypedAstNode
1515

1616
__all__ = (
@@ -23,7 +23,7 @@
2323
'SetRemove'
2424
)
2525

26-
class SetMethod(PyccelInternalFunction):
26+
class SetMethod(PyccelFunction):
2727
"""
2828
Abstract class for set method calls.
2929

pyccel/ast/builtins.py

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
from .datatypes import GenericType, PythonNativeComplex, PrimitiveComplexType
2020
from .datatypes import HomogeneousTupleType, InhomogeneousTupleType
2121
from .datatypes import HomogeneousListType, HomogeneousContainerType
22-
from .datatypes import FixedSizeNumericType, HomogeneousSetType
23-
from .internals import PyccelInternalFunction, Slice, PyccelArrayShapeElement
22+
from .datatypes import FixedSizeNumericType, HomogeneousSetType, SymbolicType
23+
from .internals import PyccelFunction, Slice, PyccelArrayShapeElement
2424
from .literals import LiteralInteger, LiteralFloat, LiteralComplex, Nil
2525
from .literals import Literal, LiteralImaginaryUnit, convert_to_literal
2626
from .literals import LiteralString
@@ -59,7 +59,7 @@
5959
)
6060

6161
#==============================================================================
62-
class PythonComplexProperty(PyccelInternalFunction):
62+
class PythonComplexProperty(PyccelFunction):
6363
"""
6464
Represents a call to the .real or .imag property.
6565
@@ -144,7 +144,7 @@ def __str__(self):
144144
return f'Imag({self.internal_var})'
145145

146146
#==============================================================================
147-
class PythonConjugate(PyccelInternalFunction):
147+
class PythonConjugate(PyccelFunction):
148148
"""
149149
Represents a call to the .conjugate() function.
150150
@@ -187,7 +187,7 @@ def __str__(self):
187187
return f'Conjugate({self.internal_var})'
188188

189189
#==============================================================================
190-
class PythonBool(PyccelInternalFunction):
190+
class PythonBool(PyccelFunction):
191191
"""
192192
Represents a call to Python's native `bool()` function.
193193
@@ -226,7 +226,7 @@ def __str__(self):
226226
return f'Bool({self.arg})'
227227

228228
#==============================================================================
229-
class PythonComplex(PyccelInternalFunction):
229+
class PythonComplex(PyccelFunction):
230230
"""
231231
Represents a call to Python's native `complex()` function.
232232
@@ -338,7 +338,7 @@ def __str__(self):
338338
return f"complex({self.real}, {self.imag})"
339339

340340
#==============================================================================
341-
class PythonEnumerate(PyccelAstNode):
341+
class PythonEnumerate(PyccelFunction):
342342
"""
343343
Represents a call to Python's native `enumerate()` function.
344344
@@ -355,6 +355,8 @@ class PythonEnumerate(PyccelAstNode):
355355
__slots__ = ('_element','_start')
356356
_attribute_nodes = ('_element','_start')
357357
name = 'enumerate'
358+
_class_type = SymbolicType()
359+
_shape = ()
358360

359361
def __init__(self, arg, start = None):
360362
if pyccel_stage != "syntactic" and \
@@ -390,7 +392,7 @@ def length(self):
390392
return PythonLen(self.element)
391393

392394
#==============================================================================
393-
class PythonFloat(PyccelInternalFunction):
395+
class PythonFloat(PyccelFunction):
394396
"""
395397
Represents a call to Python's native `float()` function.
396398
@@ -431,7 +433,7 @@ def __str__(self):
431433
return f'float({self.arg})'
432434

433435
#==============================================================================
434-
class PythonInt(PyccelInternalFunction):
436+
class PythonInt(PyccelFunction):
435437
"""
436438
Represents a call to Python's native `int()` function.
437439
@@ -613,7 +615,7 @@ def __new__(cls, arg):
613615
raise TypeError(f"Can't unpack {arg} into a tuple")
614616

615617
#==============================================================================
616-
class PythonLen(PyccelInternalFunction):
618+
class PythonLen(PyccelFunction):
617619
"""
618620
Represents a `len` expression in the code.
619621
@@ -768,12 +770,25 @@ def is_homogeneous(self):
768770
return True
769771

770772
#==============================================================================
771-
class PythonMap(PyccelAstNode):
772-
""" Represents the map stmt
773+
class PythonMap(PyccelFunction):
774+
"""
775+
Class representing a call to Python's builtin map function.
776+
777+
Class representing a call to Python's builtin map function.
778+
779+
Parameters
780+
----------
781+
func : FunctionDef
782+
The function to be applied to the elements.
783+
784+
func_args : TypedAstNode
785+
The arguments to which the function will be applied.
773786
"""
774787
__slots__ = ('_func','_func_args')
775788
_attribute_nodes = ('_func','_func_args')
776789
name = 'map'
790+
_class_type = SymbolicType()
791+
_shape = ()
777792

778793
def __init__(self, func, func_args):
779794
self._func = func
@@ -842,7 +857,7 @@ def file(self):
842857
return self._file
843858

844859
#==============================================================================
845-
class PythonRange(PyccelAstNode):
860+
class PythonRange(PyccelFunction):
846861
"""
847862
Class representing a range.
848863
@@ -862,6 +877,8 @@ class PythonRange(PyccelAstNode):
862877
__slots__ = ('_start','_stop','_step')
863878
_attribute_nodes = ('_start', '_stop', '_step')
864879
name = 'range'
880+
_class_type = SymbolicType()
881+
_shape = ()
865882

866883
def __init__(self, *args):
867884
# Define default values
@@ -917,7 +934,7 @@ def __getitem__(self, index):
917934

918935

919936
#==============================================================================
920-
class PythonZip(PyccelInternalFunction):
937+
class PythonZip(PyccelFunction):
921938
"""
922939
Represents a call to Python `zip` for code generation.
923940
@@ -957,7 +974,7 @@ def __getitem__(self, index):
957974
return [a[index] for a in self.args]
958975

959976
#==============================================================================
960-
class PythonAbs(PyccelInternalFunction):
977+
class PythonAbs(PyccelFunction):
961978
"""
962979
Represents a call to Python `abs` for code generation.
963980
@@ -985,7 +1002,7 @@ def arg(self):
9851002
return self._args[0]
9861003

9871004
#==============================================================================
988-
class PythonSum(PyccelInternalFunction):
1005+
class PythonSum(PyccelFunction):
9891006
"""
9901007
Represents a call to Python `sum` for code generation.
9911008
@@ -1019,7 +1036,7 @@ def arg(self):
10191036
return self._args[0]
10201037

10211038
#==============================================================================
1022-
class PythonMax(PyccelInternalFunction):
1039+
class PythonMax(PyccelFunction):
10231040
"""
10241041
Represents a call to Python's built-in `max` function.
10251042
@@ -1045,7 +1062,7 @@ def __init__(self, *x):
10451062

10461063
if not x.is_homogeneous:
10471064
types = ', '.join(str(xi.dtype) for xi in x)
1048-
raise PyccelError("Cannot determine final dtype of 'max' call with arguments of different "
1065+
raise TypeError("Cannot determine final dtype of 'max' call with arguments of different "
10491066
f"types ({types}). Please cast arguments to the desired dtype")
10501067
if isinstance(x.class_type, HomogeneousContainerType):
10511068
self._class_type = x.class_type.element_type
@@ -1055,11 +1072,11 @@ def __init__(self, *x):
10551072

10561073

10571074
#==============================================================================
1058-
class PythonMin(PyccelInternalFunction):
1075+
class PythonMin(PyccelFunction):
10591076
"""
1060-
Represents a call to Python's built-in `max` function.
1077+
Represents a call to Python's built-in `min` function.
10611078
1062-
Represents a call to Python's built-in `max` function.
1079+
Represents a call to Python's built-in `min` function.
10631080
10641081
Parameters
10651082
----------
@@ -1081,7 +1098,7 @@ def __init__(self, *x):
10811098

10821099
if not x.is_homogeneous:
10831100
types = ', '.join(str(xi.dtype) for xi in x)
1084-
raise PyccelError("Cannot determine final dtype of 'min' call with arguments of different "
1101+
raise TypeError("Cannot determine final dtype of 'min' call with arguments of different "
10851102
f"types ({types}). Please cast arguments to the desired dtype")
10861103
if isinstance(x.class_type, HomogeneousContainerType):
10871104
self._class_type = x.class_type.element_type
@@ -1135,7 +1152,7 @@ def __str__(self):
11351152
return f"{self.variables} -> {self.expr}"
11361153

11371154
#==============================================================================
1138-
class PythonType(PyccelAstNode):
1155+
class PythonType(PyccelFunction):
11391156
"""
11401157
Represents a call to the Python builtin `type` function.
11411158
@@ -1153,10 +1170,12 @@ class PythonType(PyccelAstNode):
11531170
"""
11541171
__slots__ = ('_type','_obj')
11551172
_attribute_nodes = ('_obj',)
1173+
_class_type = SymbolicType()
1174+
_shape = ()
11561175

11571176
def __init__(self, obj):
11581177
if not isinstance (obj, TypedAstNode):
1159-
raise PyccelError(f"Python's type function is not implemented for {type(obj)} object")
1178+
raise TypeError(f"Python's type function is not implemented for {type(obj)} object")
11601179
self._type = obj.class_type
11611180
self._obj = obj
11621181

pyccel/ast/cmathext.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from pyccel.ast.core import PyccelFunctionDef, Module
1212
from pyccel.ast.datatypes import PythonNativeBool, PythonNativeFloat, PythonNativeComplex
1313
from pyccel.ast.datatypes import PrimitiveComplexType, HomogeneousTupleType
14-
from pyccel.ast.internals import PyccelInternalFunction
14+
from pyccel.ast.internals import PyccelFunction
1515
from pyccel.ast.literals import LiteralInteger
1616
from pyccel.ast.operators import PyccelAnd, PyccelOr
1717
from pyccel.ast.variable import Constant
@@ -403,7 +403,7 @@ def __new__(cls, z):
403403
# Dictionary to map math functions to classes above
404404
#==============================================================================
405405

406-
class CmathPhase(PyccelInternalFunction):
406+
class CmathPhase(PyccelFunction):
407407
"""
408408
Class representing a call to the `cmath.phase` function.
409409
@@ -420,7 +420,7 @@ class CmathPhase(PyccelInternalFunction):
420420
def __init__(self, z):
421421
super().__init__(z)
422422

423-
class CmathPolar(PyccelInternalFunction):
423+
class CmathPolar(PyccelFunction):
424424
"""
425425
Class representing a call to the `cmath.polar` function.
426426
@@ -439,7 +439,7 @@ class CmathPolar(PyccelInternalFunction):
439439
def __init__(self, z):
440440
super().__init__(z)
441441

442-
class CmathRect(PyccelInternalFunction):
442+
class CmathRect(PyccelFunction):
443443
"""
444444
Class representing a call to the `cmath.rect` function.
445445

0 commit comments

Comments
 (0)