Skip to content

Commit 65221a9

Browse files
committed
#2923 Added type information.
1 parent 97a3e86 commit 65221a9

File tree

1 file changed

+59
-57
lines changed

1 file changed

+59
-57
lines changed

src/psyclone/psyir/backend/sympy_writer.py

Lines changed: 59 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,19 @@
3939
'''
4040

4141
import keyword
42+
from typing import Dict, List, Optional, Set, Union
4243

4344
import sympy
4445
from sympy.parsing.sympy_parser import parse_expr
4546

4647
from psyclone.psyir.backend.fortran import FortranWriter
4748
from psyclone.psyir.backend.visitor import VisitorError
49+
from psyclone.core import Signature
4850
from psyclone.psyir.frontend.sympy_reader import SymPyReader
4951
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)
5155
from psyclone.psyir.symbols import (ArrayType, ScalarType, SymbolTable)
5256

5357

@@ -96,7 +100,7 @@ class SymPyWriter(FortranWriter):
96100
# A list of all reserved Python keywords (Fortran variables that are the
97101
# same as a reserved name must be renamed, otherwise parsing will fail).
98102
# This class attribute will get initialised in __init__:
99-
_RESERVED_NAMES = set()
103+
_RESERVED_NAMES: Set[str] = set()
100104

101105
def __init__(self):
102106
super().__init__()
@@ -197,7 +201,11 @@ def __getitem__(self, _):
197201
"never be called.")
198202

199203
# -------------------------------------------------------------------------
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:
201209
'''Creates a Function class with the given name to be used for SymPy
202210
parsing. This Function overwrites the conversion to string, and will
203211
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):
206214
to the object, so that the SymPyReader can recreate the proper
207215
access to a user-defined type.
208216
209-
:param str name: name of the function class to create.
217+
:param name: name of the function class to create.
210218
:param sig: the signature of the variable, which is required
211219
to convert user defined types back properly. Only defined for
212220
user-defined types.
@@ -246,8 +254,10 @@ def _create_sympy_array_function(self, name, sig=None, num_dims=None):
246254
return new_func
247255

248256
# -------------------------------------------------------------------------
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):
251261
'''This function creates a dictionary mapping each Reference in any
252262
of the expressions to either a SymPy Function (if the reference
253263
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,
282292
:param list_of_expressions: the list of expressions from which all
283293
references are taken and added to a symbol table to avoid
284294
renaming any symbols (so that only member names will be renamed).
285-
:type list_of_expressions: List[:py:class:`psyclone.psyir.nodes.Node`]
286295
:param identical_variables: which variable names are known to represent
287296
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
290298
default) to assume that all variables are positive definite
291299
quantities.
292300
@@ -363,25 +371,24 @@ def _create_type_map(self, list_of_expressions, identical_variables=None,
363371

364372
# -------------------------------------------------------------------------
365373
@property
366-
def lower_bound(self):
374+
def lower_bound(self) -> str:
367375
''':returns: the name to be used for an unspecified lower bound.
368-
:rtype: str
369376
370377
'''
371378
return self._lower_bound
372379

373380
# -------------------------------------------------------------------------
374381
@property
375-
def upper_bound(self):
382+
def upper_bound(self) -> str:
376383
''':returns: the name to be used for an unspecified upper bound.
377-
:rtype: str
378384
379385
'''
380386
return self._upper_bound
381387

382388
# -------------------------------------------------------------------------
383389
@property
384-
def type_map(self):
390+
def type_map(self) -> Dict[str, Union[sympy.core.symbol.Symbol,
391+
sympy.core.function.Function]]:
385392
''':returns: the mapping of names to SymPy symbols or functions.
386393
:rtype: Dict[str, Union[:py:class:`sympy.core.symbol.Symbol`,
387394
:py:class:`sympy.core.function.Function`]]
@@ -390,8 +397,12 @@ def type_map(self):
390397
return self._sympy_type_map
391398

392399
# -------------------------------------------------------------------------
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]]:
395406
'''Converts PSyIR expressions to strings. It will replace Fortran-
396407
specific expressions with code that can be parsed by SymPy. The
397408
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,
403414
404415
:param identical_variables: which variable names are known to be
405416
identical
406-
:type identical_variables: Optional[dict[str, str]]
407-
408417
:param list_of_expressions: the list of expressions which are to be
409418
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
413420
default) to assume that all variables are positive definite
414421
quantities.
415422
416423
:returns: the converted strings(s).
417-
:rtype: Union[str, List[str]]
418424
419425
'''
420426
is_list = isinstance(list_of_expressions, (tuple, list))
421427
if not is_list:
428+
# Make mypy happy:
429+
assert isinstance(list_of_expressions, Node)
422430
list_of_expressions = [list_of_expressions]
423431

432+
# Make mypy happy:
433+
assert isinstance(list_of_expressions, List)
424434
# Create the type map in `self._sympy_type_map`, which is required
425435
# when converting these strings to SymPy expressions
426436
self._create_type_map(list_of_expressions,
@@ -438,8 +448,13 @@ def _to_str(self, list_of_expressions, identical_variables=None,
438448
return expression_str_list
439449

440450
# -------------------------------------------------------------------------
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]]:
443458
'''
444459
This function takes a list of PSyIR expressions, and converts
445460
them all into Sympy expressions using the SymPy parser.
@@ -454,20 +469,15 @@ def __call__(self, list_of_expressions, identical_variables=None,
454469
455470
:param list_of_expressions: the list of expressions which are to be
456471
converted into SymPy-parsable strings.
457-
:type list_of_expressions: list of
458-
:py:class:`psyclone.psyir.nodes.Node`
459472
:param identical_variables: which variable names are known to be
460473
identical
461-
:type identical_variables: Optional[dict[str, str]]
462474
:param Optional[bool] all_variables_positive: whether or not (the
463475
default) to assume that all variables are positive definite
464476
quantities.
465477
466478
:returns: a 2-tuple consisting of the the converted PSyIR
467479
expressions, followed by a dictionary mapping the symbol names
468480
to SymPy Symbols.
469-
:rtype: Union[:py:class:`sympy.core.basic.Basic`,
470-
List[:py:class:`sympy.core.basic.Basic`]]
471481
472482
:raises VisitorError: if an invalid SymPy expression is found.
473483
:raises TypeError: if the identical_variables parameter is not
@@ -486,7 +496,9 @@ def __call__(self, list_of_expressions, identical_variables=None,
486496

487497
is_list = isinstance(list_of_expressions, (tuple, list))
488498
if not is_list:
499+
assert isinstance(list_of_expressions, Node)
489500
list_of_expressions = [list_of_expressions]
501+
490502
expression_str_list = self._to_str(
491503
list_of_expressions, identical_variables=identical_variables,
492504
all_variables_positive=all_variables_positive)
@@ -506,39 +518,39 @@ def __call__(self, list_of_expressions, identical_variables=None,
506518
return result[0]
507519

508520
# -------------------------------------------------------------------------
509-
def arrayreference_node(self, node):
521+
def arrayreference_node(self, node: ArrayReference) -> str:
510522
'''The implementation of the method handling a
511523
ArrayOfStructureReference is generic enough to also handle
512524
non-structure arrays. So just use it.
513525
514526
:param node: a ArrayReference PSyIR node.
515-
:type node: :py:class:`psyclone.psyir.nodes.ArrayReference`
516527
517528
:returns: the code as string.
518-
:rtype: str
519529
520530
'''
521531
return self.arrayofstructuresreference_node(node)
522532

523533
# -------------------------------------------------------------------------
524-
def structurereference_node(self, node):
534+
def structurereference_node(self, node: StructureReference) -> str:
525535
'''The implementation of the method handling a
526536
ArrayOfStructureReference is generic enough to also handle non-arrays.
527537
So just use it.
528538
529539
:param node: a StructureReference PSyIR node.
530-
:type node: :py:class:`psyclone.psyir.nodes.StructureReference`
531540
532541
:returns: the code as string.
533-
:rtype: str
534542
535543
'''
536544
return self.arrayofstructuresreference_node(node)
537545

538546
# -------------------------------------------------------------------------
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
542554
the string ``a_b(i,i,1,j,j,1)`` (also handling name clashes in case
543555
that the user code already contains a symbol ``a_b``). The SymPy
544556
function created for this new symbol will store the original signature
@@ -548,10 +560,8 @@ def arrayofstructuresreference_node(self, node):
548560
representation
549561
550562
:param node: a StructureReference PSyIR node.
551-
:type node: :py:class:`psyclone.psyir.nodes.StructureReference`
552563
553564
:returns: the code as string.
554-
:rtype: str
555565
556566
'''
557567
sig, indices = node.get_signature_and_indices()
@@ -591,18 +601,16 @@ def arrayofstructuresreference_node(self, node):
591601
return unique_name
592602

593603
# -------------------------------------------------------------------------
594-
def literal_node(self, node):
604+
def literal_node(self, node: Literal) -> str:
595605
'''This method is called when a Literal instance is found in the PSyIR
596606
tree. For SymPy we need to handle booleans (which are expected to
597607
be capitalised: True). Real values work by just ignoring any precision
598608
information (e.g. 2_4, 3.1_wp). Character constants are not supported
599609
and will raise an exception.
600610
601611
:param node: a Literal PSyIR node.
602-
:type node: :py:class:`psyclone.psyir.nodes.Literal`
603612
604613
:returns: the SymPy representation for the literal.
605-
:rtype: str
606614
607615
:raises TypeError: if a character constant is found, which
608616
is not supported with SymPy.
@@ -621,17 +629,16 @@ def literal_node(self, node):
621629
# information can be ignored.
622630
return node.value
623631

624-
def intrinsiccall_node(self, node):
632+
# -------------------------------------------------------------------------
633+
def intrinsiccall_node(self, node: IntrinsicCall) -> str:
625634
''' This method is called when an IntrinsicCall instance is found in
626635
the PSyIR tree. The Sympy backend will use the exact sympy name for
627636
some math intrinsics (listed in _intrinsic_to_str) and will remove
628637
named arguments.
629638
630639
:param node: an IntrinsicCall PSyIR node.
631-
:type node: :py:class:`psyclone.psyir.nodes.IntrinsicCall`
632640
633641
:returns: the SymPy representation for the Intrinsic.
634-
:rtype: str
635642
636643
'''
637644
# Sympy does not support argument names, remove them for now
@@ -660,18 +667,16 @@ def intrinsiccall_node(self, node):
660667
return super().call_node(node)
661668

662669
# -------------------------------------------------------------------------
663-
def reference_node(self, node):
670+
def reference_node(self, node: Reference) -> str:
664671
'''This method is called when a Reference instance is found in the
665672
PSyIR tree. It handles the case that this normal reference might
666673
be an array expression, which in the SymPy writer needs to have
667674
indices added explicitly: it basically converts the array expression
668675
``a`` to ``a(sympy_lower, sympy_upper, 1)``.
669676
670677
:param node: a Reference PSyIR node.
671-
:type node: :py:class:`psyclone.psyir.nodes.Reference`
672678
673679
:returns: the text representation of this reference.
674-
:rtype: str
675680
676681
'''
677682
# Support renaming a symbol (e.g. if it is a reserved Python name).
@@ -706,7 +711,6 @@ def binaryoperation_node(self, node: BinaryOperation) -> str:
706711
:param node: a Reference PSyIR BinaryOperation.
707712
708713
'''
709-
710714
for psy_op, sympy_op in [(BinaryOperation.Operator.AND,
711715
"{lhs} & {rhs}"),
712716
(BinaryOperation.Operator.OR,
@@ -723,7 +727,9 @@ def binaryoperation_node(self, node: BinaryOperation) -> str:
723727
return super().binaryoperation_node(node)
724728

725729
# ------------------------------------------------------------------------
726-
def gen_indices(self, indices, var_name=None):
730+
def gen_indices(self,
731+
indices: List[Node],
732+
var_name: Optional[str] = None):
727733
'''Given a list of PSyIR nodes representing the dimensions of an
728734
array, return a list of strings representing those array dimensions.
729735
This is used both for array references and array declarations. Note
@@ -732,12 +738,10 @@ def gen_indices(self, indices, var_name=None):
732738
each array index into three parameters to support array expressions.
733739
734740
: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
737742
are created. Not used in this implementation.
738743
739744
:returns: the Fortran representation of the dimensions.
740-
:rtype: List[str]
741745
742746
:raises NotImplementedError: if the format of the dimension is not
743747
supported.
@@ -770,16 +774,14 @@ def gen_indices(self, indices, var_name=None):
770774
return dims
771775

772776
# -------------------------------------------------------------------------
773-
def range_node(self, node):
777+
def range_node(self, node: Range) -> str:
774778
'''This method is called when a Range instance is found in the PSyIR
775779
tree. This implementation converts a range into three parameters
776780
for the corresponding SymPy function.
777781
778782
:param node: a Range PSyIR node.
779-
:type node: :py:class:`psyclone.psyir.nodes.Range`
780783
781784
:returns: the Fortran code as a string.
782-
:rtype: str
783785
784786
'''
785787
if node.parent and node.parent.is_lower_bound(

0 commit comments

Comments
 (0)