# -----------------------------------------------------------------------------
# BSD 3-Clause License
#
# Copyright (c) 2017-2026, Science and Technology Facilities Council.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# -----------------------------------------------------------------------------
# Authors R. W. Ford, A. R. Porter, S. Siso and N. Nobre, STFC Daresbury Lab
# I. Kavcic, Met Office
# J. Henrichs, Bureau of Meteorology
# Modified A. B. G. Chalk, STFC Daresbury Lab
# -----------------------------------------------------------------------------
''' This module contains the Literal node implementation.'''
import re
from typing import Union
from psyclone.core import VariablesAccessMap, Signature, AccessType
from psyclone.psyir.nodes.datanode import DataNode
from psyclone.psyir.symbols.symbol import Symbol
from psyclone.psyir.symbols.datatypes import ScalarType, ArrayType
[docs]
class Literal(DataNode):
'''
Node representing a Literal. The value and datatype properties of
this node are immutable.
If the node represents "real" data and the value is expressed with
an exponent (e.g. 3.2e4 or 0.1E-3) then the stored value always uses
a lower case "e".
:param str value: the value of the literal.
:param datatype: the datatype of this literal.
:type datatype: :py:class:`psyclone.psyir.symbols.DataType`
:param parent: the parent node of this Literal in the PSyIR.
:type parent: :py:class:`psyclone.psyir.nodes.Node`
:raises TypeError: if the datatype is not an instance of \
:py:class:`psyclone.psyir.symbols.DataType`.
:raises ValueError: if the datatype is not one of self.VALID_DATA_TYPES.
:raises TypeError: if the supplied value is not a string.
:raises ValueError: if the supplied value is an empty string and the \
Literal is not a CHARACTER.
:raises ValueError: if the Literal is a BOOLEAN and the value is not \
'true' or 'false'.
:raises ValueError: if the Literal is a REAL but does not conform to \
the supported format defined by the `_real_value` property.
:raises ValueError: if the Literal is an INTEGER but does not conform \
to the supported format defined by the `_int_value` property.
'''
# Textual description of the node.
_children_valid_format = "<LeafNode>"
_text_name = "Literal"
_colour = "yellow"
_real_value = r'^[+-]?[0-9]+(\.[0-9]*)?([eE][+-]?[0-9]+)?$'
_int_value = r'([+-]?[0-9]+)'
def __init__(self, value, datatype, parent=None):
super().__init__(parent=parent)
# Checks for the datatype
if not isinstance(datatype, (ScalarType, ArrayType)):
raise TypeError(
f"The datatype of a Literal must be an instance of "
f"psyir.symbols.ScalarType or psyir.symbols.ArrayType "
f"but found '{type(datatype).__name__}'")
if not isinstance(value, str):
raise TypeError(
f"Literals must be supplied with a value encoded as a string "
f"but found '{type(value).__name__}'")
if not value and datatype.intrinsic != ScalarType.Intrinsic.CHARACTER:
raise ValueError("A non-character literal value cannot be empty.")
if (isinstance(datatype, ScalarType) and
datatype.intrinsic == ScalarType.Intrinsic.BOOLEAN and
value not in ("true", "false")):
raise ValueError(
f"A scalar boolean literal can only be: 'true' or "
f"'false' but found '{value}'.")
if datatype.intrinsic == ScalarType.Intrinsic.REAL:
if not re.match(Literal._real_value, value):
raise ValueError(
f"A scalar real literal value must conform to the "
f"supported format ('{Literal._real_value}') but found "
f"'{value}'.")
# Ensure we always store any exponent with a lowercase 'e'
self._value = value.replace("E", "e", 1)
elif isinstance(datatype, ScalarType) and datatype.intrinsic \
== ScalarType.Intrinsic.INTEGER:
if not re.fullmatch(Literal._int_value, value):
raise ValueError(
f"A scalar integer literal value must conform to the "
f"supported format ('{Literal._int_value}') but found "
f"'{value}'.")
self._value = value
else:
self._value = value
self._datatype = datatype
def __eq__(self, other):
'''Checks the equality of this Literal with other. Literals are
equal if they are the same type, and have the same datatype and
value string (for now only compared with ==).
:param object other: the object to check equality to.
:returns: whether other is equal to self.
:rtype: bool
'''
is_eq = super().__eq__(other)
is_eq = is_eq and self.datatype == other.datatype
is_eq = is_eq and self.value == other.value
return is_eq
@property
def datatype(self):
'''
:returns: the type of this Literal.
:rtype: :py:class:`psyclone.psyir.symbols.DataType`
'''
return self._datatype
@property
def value(self):
'''
:returns: String representing the literal value.
:rtype: str
'''
return self._value
[docs]
def node_str(self, colour=True):
'''
Construct a text representation of this node, optionally containing
colour control codes.
:param bool colour: whether or not to include colour control codes.
:returns: description of this PSyIR node.
:rtype: str
'''
return (f"{self.coloured_name(colour)}"
f"[value:'{self._value}', {self.datatype}]")
[docs]
def get_all_accessed_symbols(self) -> set[Symbol]:
'''
:returns: a set of all the symbols accessed inside this Literal.
'''
symbols = super().get_all_accessed_symbols()
if isinstance(self.datatype.precision, DataNode):
symbols.update(self.datatype.get_all_accessed_symbols())
return symbols
[docs]
def reference_accesses(self) -> VariablesAccessMap:
'''
:returns: a map of all the symbol accessed inside this node, the
keys are Signatures (unique identifiers to a symbol and its
structure accessors) and the values are AccessSequence
(a sequence of AccessTypes).
'''
access_info = VariablesAccessMap()
if isinstance(self.datatype.precision, DataNode):
precision_symbols = self.datatype.get_all_accessed_symbols()
for symbol in precision_symbols:
access_info.add_access(
Signature(symbol.name), AccessType.CONSTANT, self)
return access_info
[docs]
def replace_symbols_using(self, table_or_symbol):
'''
Replace any Symbols referred to by this object with those in the
supplied SymbolTable (or just the supplied Symbol instance) if they
have matching names. If there is no match for a given Symbol then it
is left unchanged.
:param table_or_symbol: the symbol table from which to get replacement
symbols or a single, replacement Symbol.
:type table_or_symbol: :py:class:`psyclone.psyir.symbols.SymbolTable` |
:py:class:`psyclone.psyir.symbols.Symbol`
'''
self.datatype.replace_symbols_using(table_or_symbol)
super().replace_symbols_using(table_or_symbol)
@property
def value_as_python(self) -> Union[str, bool, int, float]:
'''
.. warning::
value_as_python doesn't attempt to preserve the precision
of the Literal, merely gives a python representation of
the value of the Literal object.
:returns: the python representation of this Literal.
'''
if self.datatype.intrinsic == ScalarType.Intrinsic.INTEGER:
return int(self.value)
if self.datatype.intrinsic == ScalarType.Intrinsic.REAL:
return float(self.value)
if self.datatype.intrinsic == ScalarType.Intrinsic.BOOLEAN:
return self.value == "true"
if self.datatype.intrinsic == ScalarType.Intrinsic.CHARACTER:
return self.value
# For AutoAPI documentation generation
__all__ = ['Literal']