39
39
'''
40
40
41
41
import keyword
42
+ from typing import Dict , List , Optional , Set , Union
42
43
43
44
import sympy
44
45
from sympy .parsing .sympy_parser import parse_expr
45
46
46
47
from psyclone .psyir .backend .fortran import FortranWriter
47
48
from psyclone .psyir .backend .visitor import VisitorError
49
+ from psyclone .core import Signature
48
50
from psyclone .psyir .frontend .sympy_reader import SymPyReader
49
51
from psyclone .psyir .nodes import (
50
- DataNode , Range , Reference , IntrinsicCall , Call )
52
+ ArrayOfStructuresReference , ArrayReference , BinaryOperation , Call ,
53
+ DataNode , IntrinsicCall , Literal , Node ,
54
+ Range , Reference , StructureReference )
51
55
from psyclone .psyir .symbols import (ArrayType , ScalarType , SymbolTable )
52
56
53
57
@@ -96,7 +100,7 @@ class SymPyWriter(FortranWriter):
96
100
# A list of all reserved Python keywords (Fortran variables that are the
97
101
# same as a reserved name must be renamed, otherwise parsing will fail).
98
102
# This class attribute will get initialised in __init__:
99
- _RESERVED_NAMES = set ()
103
+ _RESERVED_NAMES : Set [ str ] = set ()
100
104
101
105
def __init__ (self ):
102
106
super ().__init__ ()
@@ -197,7 +201,11 @@ def __getitem__(self, _):
197
201
"never be called." )
198
202
199
203
# -------------------------------------------------------------------------
200
- def _create_sympy_array_function (self , name , sig = None , num_dims = None ):
204
+ def _create_sympy_array_function (
205
+ self ,
206
+ name : str ,
207
+ sig : Optional [Signature ] = None ,
208
+ num_dims : Optional [List [int ]] = None ) -> sympy .Function :
201
209
'''Creates a Function class with the given name to be used for SymPy
202
210
parsing. This Function overwrites the conversion to string, and will
203
211
replace the triplicated array indices back to the normal Fortran
@@ -206,7 +214,7 @@ def _create_sympy_array_function(self, name, sig=None, num_dims=None):
206
214
to the object, so that the SymPyReader can recreate the proper
207
215
access to a user-defined type.
208
216
209
- :param str name: name of the function class to create.
217
+ :param name: name of the function class to create.
210
218
:param sig: the signature of the variable, which is required
211
219
to convert user defined types back properly. Only defined for
212
220
user-defined types.
@@ -246,8 +254,10 @@ def _create_sympy_array_function(self, name, sig=None, num_dims=None):
246
254
return new_func
247
255
248
256
# -------------------------------------------------------------------------
249
- def _create_type_map (self , list_of_expressions , identical_variables = None ,
250
- all_variables_positive = None ):
257
+ def _create_type_map (self ,
258
+ list_of_expressions : List [Node ],
259
+ identical_variables : Optional [Dict [str , str ]] = None ,
260
+ all_variables_positive : Optional [bool ] = None ):
251
261
'''This function creates a dictionary mapping each Reference in any
252
262
of the expressions to either a SymPy Function (if the reference
253
263
is an array reference) or a Symbol (if the reference is not an
@@ -282,11 +292,9 @@ def _create_type_map(self, list_of_expressions, identical_variables=None,
282
292
:param list_of_expressions: the list of expressions from which all
283
293
references are taken and added to a symbol table to avoid
284
294
renaming any symbols (so that only member names will be renamed).
285
- :type list_of_expressions: List[:py:class:`psyclone.psyir.nodes.Node`]
286
295
:param identical_variables: which variable names are known to represent
287
296
identical quantities.
288
- :type identical_variables: Optional[dict[str, str]]
289
- :param Optional[bool] all_variables_positive: whether or not (the
297
+ :param all_variables_positive: whether or not (the
290
298
default) to assume that all variables are positive definite
291
299
quantities.
292
300
@@ -363,25 +371,24 @@ def _create_type_map(self, list_of_expressions, identical_variables=None,
363
371
364
372
# -------------------------------------------------------------------------
365
373
@property
366
- def lower_bound (self ):
374
+ def lower_bound (self ) -> str :
367
375
''':returns: the name to be used for an unspecified lower bound.
368
- :rtype: str
369
376
370
377
'''
371
378
return self ._lower_bound
372
379
373
380
# -------------------------------------------------------------------------
374
381
@property
375
- def upper_bound (self ):
382
+ def upper_bound (self ) -> str :
376
383
''':returns: the name to be used for an unspecified upper bound.
377
- :rtype: str
378
384
379
385
'''
380
386
return self ._upper_bound
381
387
382
388
# -------------------------------------------------------------------------
383
389
@property
384
- def type_map (self ):
390
+ def type_map (self ) -> Dict [str , Union [sympy .core .symbol .Symbol ,
391
+ sympy .core .function .Function ]]:
385
392
''':returns: the mapping of names to SymPy symbols or functions.
386
393
:rtype: Dict[str, Union[:py:class:`sympy.core.symbol.Symbol`,
387
394
:py:class:`sympy.core.function.Function`]]
@@ -390,8 +397,12 @@ def type_map(self):
390
397
return self ._sympy_type_map
391
398
392
399
# -------------------------------------------------------------------------
393
- def _to_str (self , list_of_expressions , identical_variables = None ,
394
- all_variables_positive = False ):
400
+ def _to_str (
401
+ self ,
402
+ list_of_expressions : Union [Node , List [Node ]],
403
+ identical_variables : Optional [Dict [str , str ]] = None ,
404
+ all_variables_positive : Optional [bool ] = False ) -> Union [str ,
405
+ List [str ]]:
395
406
'''Converts PSyIR expressions to strings. It will replace Fortran-
396
407
specific expressions with code that can be parsed by SymPy. The
397
408
argument can either be a single element (in which case a single string
@@ -403,24 +414,23 @@ def _to_str(self, list_of_expressions, identical_variables=None,
403
414
404
415
:param identical_variables: which variable names are known to be
405
416
identical
406
- :type identical_variables: Optional[dict[str, str]]
407
-
408
417
:param list_of_expressions: the list of expressions which are to be
409
418
converted into SymPy-parsable strings.
410
- :type list_of_expressions: Union[:py:class:`psyclone.psyir.nodes.Node`,
411
- List[:py:class:`psyclone.psyir.nodes.Node`]]
412
- :param Optional[bool] all_variables_positive: whether or not (the
419
+ :param all_variables_positive: whether or not (the
413
420
default) to assume that all variables are positive definite
414
421
quantities.
415
422
416
423
:returns: the converted strings(s).
417
- :rtype: Union[str, List[str]]
418
424
419
425
'''
420
426
is_list = isinstance (list_of_expressions , (tuple , list ))
421
427
if not is_list :
428
+ # Make mypy happy:
429
+ assert isinstance (list_of_expressions , Node )
422
430
list_of_expressions = [list_of_expressions ]
423
431
432
+ # Make mypy happy:
433
+ assert isinstance (list_of_expressions , List )
424
434
# Create the type map in `self._sympy_type_map`, which is required
425
435
# when converting these strings to SymPy expressions
426
436
self ._create_type_map (list_of_expressions ,
@@ -438,8 +448,13 @@ def _to_str(self, list_of_expressions, identical_variables=None,
438
448
return expression_str_list
439
449
440
450
# -------------------------------------------------------------------------
441
- def __call__ (self , list_of_expressions , identical_variables = None ,
442
- all_variables_positive = False ):
451
+ def __call__ (
452
+ self ,
453
+ list_of_expressions : Union [Node , List [Node ]],
454
+ identical_variables : Optional [Dict [str , str ]] = None ,
455
+ all_variables_positive : Optional [bool ] = False ) \
456
+ -> Union [sympy .core .basic .Basic ,
457
+ List [sympy .core .basic .Basic ]]:
443
458
'''
444
459
This function takes a list of PSyIR expressions, and converts
445
460
them all into Sympy expressions using the SymPy parser.
@@ -454,20 +469,15 @@ def __call__(self, list_of_expressions, identical_variables=None,
454
469
455
470
:param list_of_expressions: the list of expressions which are to be
456
471
converted into SymPy-parsable strings.
457
- :type list_of_expressions: list of
458
- :py:class:`psyclone.psyir.nodes.Node`
459
472
:param identical_variables: which variable names are known to be
460
473
identical
461
- :type identical_variables: Optional[dict[str, str]]
462
474
:param Optional[bool] all_variables_positive: whether or not (the
463
475
default) to assume that all variables are positive definite
464
476
quantities.
465
477
466
478
:returns: a 2-tuple consisting of the the converted PSyIR
467
479
expressions, followed by a dictionary mapping the symbol names
468
480
to SymPy Symbols.
469
- :rtype: Union[:py:class:`sympy.core.basic.Basic`,
470
- List[:py:class:`sympy.core.basic.Basic`]]
471
481
472
482
:raises VisitorError: if an invalid SymPy expression is found.
473
483
:raises TypeError: if the identical_variables parameter is not
@@ -486,7 +496,9 @@ def __call__(self, list_of_expressions, identical_variables=None,
486
496
487
497
is_list = isinstance (list_of_expressions , (tuple , list ))
488
498
if not is_list :
499
+ assert isinstance (list_of_expressions , Node )
489
500
list_of_expressions = [list_of_expressions ]
501
+
490
502
expression_str_list = self ._to_str (
491
503
list_of_expressions , identical_variables = identical_variables ,
492
504
all_variables_positive = all_variables_positive )
@@ -506,39 +518,39 @@ def __call__(self, list_of_expressions, identical_variables=None,
506
518
return result [0 ]
507
519
508
520
# -------------------------------------------------------------------------
509
- def arrayreference_node (self , node ) :
521
+ def arrayreference_node (self , node : ArrayReference ) -> str :
510
522
'''The implementation of the method handling a
511
523
ArrayOfStructureReference is generic enough to also handle
512
524
non-structure arrays. So just use it.
513
525
514
526
:param node: a ArrayReference PSyIR node.
515
- :type node: :py:class:`psyclone.psyir.nodes.ArrayReference`
516
527
517
528
:returns: the code as string.
518
- :rtype: str
519
529
520
530
'''
521
531
return self .arrayofstructuresreference_node (node )
522
532
523
533
# -------------------------------------------------------------------------
524
- def structurereference_node (self , node ) :
534
+ def structurereference_node (self , node : StructureReference ) -> str :
525
535
'''The implementation of the method handling a
526
536
ArrayOfStructureReference is generic enough to also handle non-arrays.
527
537
So just use it.
528
538
529
539
:param node: a StructureReference PSyIR node.
530
- :type node: :py:class:`psyclone.psyir.nodes.StructureReference`
531
540
532
541
:returns: the code as string.
533
- :rtype: str
534
542
535
543
'''
536
544
return self .arrayofstructuresreference_node (node )
537
545
538
546
# -------------------------------------------------------------------------
539
- def arrayofstructuresreference_node (self , node ):
540
- '''This handles ArrayOfStructureReferences (and also simple
541
- StructureReferences). An access like ``a(i)%b(j)`` is converted to
547
+ def arrayofstructuresreference_node (
548
+ self ,
549
+ node : Union [ArrayOfStructuresReference ,
550
+ ArrayReference ,
551
+ StructureReference ]) -> str :
552
+ '''This handles ArrayOfStructuresReference (and also simple
553
+ StructureReference). An access like ``a(i)%b(j)`` is converted to
542
554
the string ``a_b(i,i,1,j,j,1)`` (also handling name clashes in case
543
555
that the user code already contains a symbol ``a_b``). The SymPy
544
556
function created for this new symbol will store the original signature
@@ -548,10 +560,8 @@ def arrayofstructuresreference_node(self, node):
548
560
representation
549
561
550
562
:param node: a StructureReference PSyIR node.
551
- :type node: :py:class:`psyclone.psyir.nodes.StructureReference`
552
563
553
564
:returns: the code as string.
554
- :rtype: str
555
565
556
566
'''
557
567
sig , indices = node .get_signature_and_indices ()
@@ -591,18 +601,16 @@ def arrayofstructuresreference_node(self, node):
591
601
return unique_name
592
602
593
603
# -------------------------------------------------------------------------
594
- def literal_node (self , node ) :
604
+ def literal_node (self , node : Literal ) -> str :
595
605
'''This method is called when a Literal instance is found in the PSyIR
596
606
tree. For SymPy we need to handle booleans (which are expected to
597
607
be capitalised: True). Real values work by just ignoring any precision
598
608
information (e.g. 2_4, 3.1_wp). Character constants are not supported
599
609
and will raise an exception.
600
610
601
611
:param node: a Literal PSyIR node.
602
- :type node: :py:class:`psyclone.psyir.nodes.Literal`
603
612
604
613
:returns: the SymPy representation for the literal.
605
- :rtype: str
606
614
607
615
:raises TypeError: if a character constant is found, which
608
616
is not supported with SymPy.
@@ -621,17 +629,16 @@ def literal_node(self, node):
621
629
# information can be ignored.
622
630
return node .value
623
631
624
- def intrinsiccall_node (self , node ):
632
+ # -------------------------------------------------------------------------
633
+ def intrinsiccall_node (self , node : IntrinsicCall ) -> str :
625
634
''' This method is called when an IntrinsicCall instance is found in
626
635
the PSyIR tree. The Sympy backend will use the exact sympy name for
627
636
some math intrinsics (listed in _intrinsic_to_str) and will remove
628
637
named arguments.
629
638
630
639
:param node: an IntrinsicCall PSyIR node.
631
- :type node: :py:class:`psyclone.psyir.nodes.IntrinsicCall`
632
640
633
641
:returns: the SymPy representation for the Intrinsic.
634
- :rtype: str
635
642
636
643
'''
637
644
# Sympy does not support argument names, remove them for now
@@ -660,18 +667,16 @@ def intrinsiccall_node(self, node):
660
667
return super ().call_node (node )
661
668
662
669
# -------------------------------------------------------------------------
663
- def reference_node (self , node ) :
670
+ def reference_node (self , node : Reference ) -> str :
664
671
'''This method is called when a Reference instance is found in the
665
672
PSyIR tree. It handles the case that this normal reference might
666
673
be an array expression, which in the SymPy writer needs to have
667
674
indices added explicitly: it basically converts the array expression
668
675
``a`` to ``a(sympy_lower, sympy_upper, 1)``.
669
676
670
677
:param node: a Reference PSyIR node.
671
- :type node: :py:class:`psyclone.psyir.nodes.Reference`
672
678
673
679
:returns: the text representation of this reference.
674
- :rtype: str
675
680
676
681
'''
677
682
# Support renaming a symbol (e.g. if it is a reserved Python name).
@@ -706,7 +711,6 @@ def binaryoperation_node(self, node: BinaryOperation) -> str:
706
711
:param node: a Reference PSyIR BinaryOperation.
707
712
708
713
'''
709
-
710
714
for psy_op , sympy_op in [(BinaryOperation .Operator .AND ,
711
715
"{lhs} & {rhs}" ),
712
716
(BinaryOperation .Operator .OR ,
@@ -723,7 +727,9 @@ def binaryoperation_node(self, node: BinaryOperation) -> str:
723
727
return super ().binaryoperation_node (node )
724
728
725
729
# ------------------------------------------------------------------------
726
- def gen_indices (self , indices , var_name = None ):
730
+ def gen_indices (self ,
731
+ indices : List [Node ],
732
+ var_name : Optional [str ] = None ):
727
733
'''Given a list of PSyIR nodes representing the dimensions of an
728
734
array, return a list of strings representing those array dimensions.
729
735
This is used both for array references and array declarations. Note
@@ -732,12 +738,10 @@ def gen_indices(self, indices, var_name=None):
732
738
each array index into three parameters to support array expressions.
733
739
734
740
:param indices: list of PSyIR nodes.
735
- :type indices: List[:py:class:`psyclone.psyir.symbols.Node`]
736
- :param str var_name: name of the variable for which the dimensions
741
+ :param var_name: name of the variable for which the dimensions
737
742
are created. Not used in this implementation.
738
743
739
744
:returns: the Fortran representation of the dimensions.
740
- :rtype: List[str]
741
745
742
746
:raises NotImplementedError: if the format of the dimension is not
743
747
supported.
@@ -770,16 +774,14 @@ def gen_indices(self, indices, var_name=None):
770
774
return dims
771
775
772
776
# -------------------------------------------------------------------------
773
- def range_node (self , node ) :
777
+ def range_node (self , node : Range ) -> str :
774
778
'''This method is called when a Range instance is found in the PSyIR
775
779
tree. This implementation converts a range into three parameters
776
780
for the corresponding SymPy function.
777
781
778
782
:param node: a Range PSyIR node.
779
- :type node: :py:class:`psyclone.psyir.nodes.Range`
780
783
781
784
:returns: the Fortran code as a string.
782
- :rtype: str
783
785
784
786
'''
785
787
if node .parent and node .parent .is_lower_bound (
0 commit comments