"""
Expressions
===========
Lyra's internal representation of Python expressions.
:Authors: Caterina Urban and Simon Wehrli
"""
from abc import ABCMeta, abstractmethod
from enum import IntEnum
from typing import Set, List
from lyra.core.types import LyraType, StringLyraType, IntegerLyraType, BooleanLyraType, \
DictLyraType, SetLyraType, ListLyraType, TupleLyraType
from lyra.core.utils import copy_docstring
[docs]class Expression(metaclass=ABCMeta):
"""Expression representation.
https://docs.python.org/3.4/reference/expressions.html
"""
def __init__(self, typ: LyraType):
"""Expression construction.
:param typ: (result) type of the expression
"""
self._typ = typ
@property
def typ(self):
return self._typ
@abstractmethod
def __eq__(self, other: 'Expression'):
"""Expression equality.
:param other: other expression to compare
:return: whether the expression equality holds
"""
@abstractmethod
def __hash__(self):
"""Expression hash representation.
:return: hash value representing the expression
"""
def __ne__(self, other: 'Expression'):
return not (self == other)
@abstractmethod
def __str__(self):
"""Expression string representation.
:return: string representing the expression
"""
[docs] def ids(self) -> Set['VariableIdentifier']:
"""Identifiers that appear in the expression.
:return: set of identifiers that appear in the expression
"""
ids = set()
for expr in _walk(self):
if isinstance(expr, VariableIdentifier):
ids.add(expr)
return ids
def _iter_child_exprs(expr: Expression):
"""
Yield all direct child expressions of ``expr``,
that is, all fields that are expressions
and all items of fields that are lists of expressions.
"""
for _, field in expr.__dict__.items():
if isinstance(field, Expression):
yield field
elif isinstance(field, list):
for item in field:
if isinstance(item, Expression):
yield item
def _walk(expr: Expression):
"""
Recursively yield all expressions in an expression tree
starting at ``expr`` (including ``expr`` itself),
in no specified order.
"""
from collections import deque
todo = deque([expr])
while todo:
expr = todo.popleft()
todo.extend(_iter_child_exprs(expr))
yield expr
# noinspection PyPep8Naming
[docs]class ExpressionVisitor(metaclass=ABCMeta):
"""
An expression visitor base class that walks the expression tree and calls a
visitor function for every expression found. This function may return a value
which is forwarded by the `visit` method.
Subclasses are meant to implement the visitor functions.
The visitor function for an expression is ``'visit_'`` +
class name of the expression. So a `Literal` expression visit function would
be `visit_Literal`. If no visitor function exists for an expression
a `NotImplementedError` is raised.
Adapted from `ast.py`.
"""
[docs] def visit(self, expr, *args, **kwargs):
"""Visit of an expression."""
method = 'visit_' + expr.__class__.__name__
if hasattr(self, method):
return getattr(self, method)(expr, *args, **kwargs)
error = f"Missing visitor for {expr.__class__.__name__} in {self.__class__.__qualname__}!"
raise NotImplementedError(error)
[docs] @abstractmethod
def visit_Literal(self, expr: 'Literal'):
"""Visit of a literal expression."""
[docs] @abstractmethod
def visit_VariableIdentifier(self, expr: 'VariableIdentifier'):
"""Visit of a variable identifier."""
[docs] @abstractmethod
def visit_LengthIdentifier(self, expr: 'LengthIdentifier'):
"""Visit of a sequence or collection length."""
[docs] @abstractmethod
def visit_ListDisplay(self, expr: 'ListDisplay'):
"""Visit of a list display."""
[docs] @abstractmethod
def visit_TupleDisplay(self, expr: 'TupleDisplay'):
"""Visit of a tuple display."""
[docs] @abstractmethod
def visit_SetDisplay(self, expr: 'SetDisplay'):
"""Visit of a set display."""
[docs] @abstractmethod
def visit_DictDisplay(self, expr: 'DictDisplay'):
"""Visit of dictionary display."""
[docs] @abstractmethod
def visit_AttributeReference(self, expr: 'AttributeReference'):
"""Visit of an attribute reference."""
[docs] @abstractmethod
def visit_Subscription(self, expr: 'Subscription'):
"""Visit of a subscription expression."""
[docs] @abstractmethod
def visit_Slicing(self, expr: 'Slicing'):
"""Visit of a slicing expression."""
[docs] @abstractmethod
def visit_Range(self, expr: 'Range'):
"""Visit of a range call expression."""
[docs] @abstractmethod
def visit_UnaryArithmeticOperation(self, expr: 'UnaryArithmeticOperation'):
"""Visit of a unary arithmetic operation."""
[docs] @abstractmethod
def visit_UnaryBooleanOperation(self, expr: 'UnaryBooleanOperation'):
"""Visit of a unary boolean operation."""
[docs] @abstractmethod
def visit_BinaryArithmeticOperation(self, expr: 'BinaryArithmeticOperation'):
"""Visit of a binary arithmetic operation."""
[docs] @abstractmethod
def visit_BinarySequenceOperation(self, expr: 'BinarySequenceOperation'):
"""Visit of a binary sequence operation."""
[docs] @abstractmethod
def visit_BinaryBooleanOperation(self, expr: 'BinaryBooleanOperation'):
"""Visit of a binary boolean operation."""
[docs] @abstractmethod
def visit_BinaryComparisonOperation(self, expr: 'BinaryComparisonOperation'):
"""Visit of a binary comparison operation."""
[docs] def generic_visit(self, expr, *args, **kwargs):
raise ValueError(
f"{self.__class__.__qualname__} does not support generic visit of expressions! "
f"Define handling for a {expr.__class__.__name__} expression explicitly!")
[docs]class NegationFreeNormalExpression(ExpressionVisitor):
"""
An expression visitor that:
1. removes negations using De Morgan's law, and
2. puts all boolean comparison operations with ``=``, ``!=``, ``<``, ``<=``, ``>``, and ``>=``
in the normal form ``expr <= 0``.
"""
[docs] @copy_docstring(ExpressionVisitor.visit_Literal)
def visit_Literal(self, expr: 'Literal', invert=False):
return expr # nothing to be done
[docs] @copy_docstring(ExpressionVisitor.visit_VariableIdentifier)
def visit_VariableIdentifier(self, expr: 'VariableIdentifier', invert=False):
if isinstance(expr.typ, BooleanLyraType) and invert:
operator = UnaryBooleanOperation.Operator.Neg
return UnaryBooleanOperation(BooleanLyraType(), operator, expr)
return expr # nothing to be done
[docs] @copy_docstring(ExpressionVisitor.visit_LengthIdentifier)
def visit_LengthIdentifier(self, expr: 'LengthIdentifier', invert=False):
return expr # nothing to be done
[docs] @copy_docstring(ExpressionVisitor.visit_ListDisplay)
def visit_ListDisplay(self, expr: 'ListDisplay', invert=False):
return expr # nothing to be done
[docs] @copy_docstring(ExpressionVisitor.visit_TupleDisplay)
def visit_TupleDisplay(self, expr: 'TupleDisplay', invert=False):
return expr # nothing to be done
[docs] @copy_docstring(ExpressionVisitor.visit_SetDisplay)
def visit_SetDisplay(self, expr: 'SetDisplay', invert=False):
return expr # nothing to be done
[docs] @copy_docstring(ExpressionVisitor.visit_DictDisplay)
def visit_DictDisplay(self, expr: 'DictDisplay', invert=False):
return expr # nothing to be done
[docs] @copy_docstring(ExpressionVisitor.visit_AttributeReference)
def visit_AttributeReference(self, expr: 'AttributeReference', invert=False):
return expr # nothing to be done
[docs] @copy_docstring(ExpressionVisitor.visit_Subscription)
def visit_Subscription(self, expr: 'Subscription', invert=False):
return expr # nothing to be done
[docs] @copy_docstring(ExpressionVisitor.visit_Slicing)
def visit_Slicing(self, expr: 'Slicing', invert=False):
return expr # nothing to be done
[docs] @copy_docstring(ExpressionVisitor.visit_Range)
def visit_Range(self, expr: 'Range', invert=False):
return expr # nothing to be done
[docs] @copy_docstring(ExpressionVisitor.visit_UnaryArithmeticOperation)
def visit_UnaryArithmeticOperation(self, expr: 'UnaryArithmeticOperation', invert=False):
return expr # nothing to be done
[docs] @copy_docstring(ExpressionVisitor.visit_UnaryBooleanOperation)
def visit_UnaryBooleanOperation(self, expr: 'UnaryBooleanOperation', invert=False):
if expr.operator == UnaryBooleanOperation.Operator.Neg:
return self.visit(expr.expression, invert=not invert)
raise ValueError(f"Unary boolean operator {expr.operator} is unsupported!")
[docs] @copy_docstring(ExpressionVisitor.visit_BinaryArithmeticOperation)
def visit_BinaryArithmeticOperation(self, expr: 'BinaryArithmeticOperation', invert=False):
return expr # nothing to be done
[docs] @copy_docstring(ExpressionVisitor.visit_BinarySequenceOperation)
def visit_BinarySequenceOperation(self, expr: 'BinarySequenceOperation', invert=False):
return expr # nothing to be done
[docs] @copy_docstring(ExpressionVisitor.visit_BinaryBooleanOperation)
def visit_BinaryBooleanOperation(self, expr: 'BinaryBooleanOperation', invert=False):
left = self.visit(expr.left, invert)
operator = expr.operator.reverse_operator() if invert else expr.operator
right = self.visit(expr.right, invert)
return BinaryBooleanOperation(expr.typ, left, operator, right)
[docs] @copy_docstring(ExpressionVisitor.visit_BinaryComparisonOperation)
def visit_BinaryComparisonOperation(self, expr: 'BinaryComparisonOperation', invert=False):
left = expr.left
operator = expr.operator.reverse_operator() if invert else expr.operator
right = expr.right
if operator == BinaryComparisonOperation.Operator.Eq:
# left = right -> left - right <= 0 && right - left <= 0
zero = Literal(IntegerLyraType(), "0")
minus = BinaryArithmeticOperation.Operator.Sub
operator = BinaryComparisonOperation.Operator.LtE
expr1 = BinaryArithmeticOperation(left.typ, left, minus, right)
expr1 = BinaryComparisonOperation(expr.typ, expr1, operator, zero)
expr2 = BinaryArithmeticOperation(right.typ, right, minus, left)
expr2 = BinaryComparisonOperation(expr.typ, expr2, operator, zero)
conjunction = BinaryBooleanOperation.Operator.And
return BinaryBooleanOperation(expr.typ, expr1, conjunction, expr2)
elif operator == BinaryComparisonOperation.Operator.NotEq:
# left != right -> left - (right - 1) <= 0 || right - (left - 1) <= 0
zero = Literal(IntegerLyraType(), "0")
one = Literal(IntegerLyraType(), "1")
minus = BinaryArithmeticOperation.Operator.Sub
operator = BinaryComparisonOperation.Operator.LtE
expr1 = BinaryArithmeticOperation(right.typ, right, minus, one)
expr1 = BinaryArithmeticOperation(left.typ, left, minus, expr1)
expr1 = BinaryComparisonOperation(expr.typ, expr1, operator, zero)
expr2 = BinaryArithmeticOperation(left.typ, left, minus, one)
expr2 = BinaryArithmeticOperation(right.typ, right, minus, expr2)
expr2 = BinaryComparisonOperation(expr.typ, expr2, operator, zero)
disjunction = BinaryBooleanOperation.Operator.Or
return BinaryBooleanOperation(expr.typ, expr1, disjunction, expr2)
elif operator == BinaryComparisonOperation.Operator.Lt:
# left < right -> left - (right - 1) <= 0
zero = Literal(IntegerLyraType(), "0")
one = Literal(IntegerLyraType(), "1")
minus = BinaryArithmeticOperation.Operator.Sub
right = BinaryArithmeticOperation(right.typ, right, minus, one)
left = BinaryArithmeticOperation(left.typ, left, minus, right)
operator = BinaryComparisonOperation.Operator.LtE
return BinaryComparisonOperation(expr.typ, left, operator, zero)
elif operator == BinaryComparisonOperation.Operator.LtE:
# left <= right -> left - right <= 0
zero = Literal(IntegerLyraType(), "0")
minus = BinaryArithmeticOperation.Operator.Sub
left = BinaryArithmeticOperation(left.typ, left, minus, right)
operator = BinaryComparisonOperation.Operator.LtE
return BinaryComparisonOperation(expr.typ, left, operator, zero)
elif operator == BinaryComparisonOperation.Operator.Gt:
# left > right -> right - (left - 1) <= 0
zero = Literal(IntegerLyraType(), "0")
one = Literal(IntegerLyraType(), "1")
minus = BinaryArithmeticOperation.Operator.Sub
left = BinaryArithmeticOperation(left.typ, left, minus, one)
right = BinaryArithmeticOperation(right.typ, right, minus, left)
operator = BinaryComparisonOperation.Operator.LtE
return BinaryComparisonOperation(expr.typ, right, operator, zero)
elif operator == BinaryComparisonOperation.Operator.GtE:
# left >= right -> right - left <= 0
zero = Literal(IntegerLyraType(), "0")
minus = BinaryArithmeticOperation.Operator.Sub
right = BinaryArithmeticOperation(right.typ, right, minus, left)
operator = BinaryComparisonOperation.Operator.LtE
return BinaryComparisonOperation(expr.typ, right, operator, zero)
elif operator == BinaryComparisonOperation.Operator.In:
return BinaryComparisonOperation(expr.typ, left, operator, right)
elif operator == BinaryComparisonOperation.Operator.NotIn:
return BinaryComparisonOperation(expr.typ, left, operator, right)
raise ValueError(f"Boolean comparison operator {expr} is unsupported!")
"""
Atomic Expressions
https://docs.python.org/3.4/reference/expressions.html#atoms
"""
[docs]class Literal(Expression):
"""Literal representation.
https://docs.python.org/3.4/reference/expressions.html#literals
"""
def __init__(self, typ: LyraType, val: str):
"""Literal construction.
:param typ: type of the literal
:param val: value of the literal
"""
super().__init__(typ)
self._val = val
@property
def val(self):
return self._val
def __eq__(self, other: 'Literal'):
return (self.typ, self.val) == (other.typ, other.val)
def __hash__(self):
return hash((self.typ, self.val))
def __str__(self):
if isinstance(self.typ, StringLyraType):
return f'"{self.val}"'
return f"{self.val}"
[docs]class Identifier(Expression, metaclass=ABCMeta):
"""Identifier representation.
https://docs.python.org/3.4/reference/expressions.html#atom-identifiers
"""
def __init__(self, typ: LyraType, name: str):
"""Identifier construction.
:param typ: type of the identifier
:param name: name of the identifier
"""
super().__init__(typ)
self._name = name
@property
def name(self):
return self._name
def __eq__(self, other: 'Identifier'):
return self.name == other.name
def __hash__(self):
return hash(self.name)
def __str__(self):
return "{0.name}".format(self)
[docs]class VariableIdentifier(Identifier):
"""Variable identifier representation."""
def __init__(self, typ: LyraType, name: str):
"""Variable identifier construction.
:param typ: type of the identifier
:param name: name of the identifier
"""
super().__init__(typ, name)
[docs]class LengthIdentifier(Identifier):
"""Sequence or collection length representation."""
def __init__(self, variable: VariableIdentifier):
"""Sequence or collection length construction.
:param variable: sequence or collection the length of which is being constructed
"""
name = "len({0.name})".format(variable)
super().__init__(IntegerLyraType(), name)
self._variable = variable
@property
def variable(self):
return self._variable
[docs]class ListDisplay(Expression):
"""List display representation.
https://docs.python.org/3/reference/expressions.html#list-displays
"""
def __init__(self, typ: ListLyraType, items: List[Expression] = None):
"""List display construction.
:param typ: type of the list
:param items: list of items being displayed
"""
super().__init__(typ)
self._items = items or []
@property
def items(self):
return self._items
def __eq__(self, other: 'ListDisplay'):
return (self.typ, self.items) == (other.typ, other.items)
def __hash__(self):
return hash((self.typ, str(self.items)))
def __str__(self):
return str(self.items)
[docs]class TupleDisplay(Expression):
"""Tuple display (= expression list with comma or ()) representation.
https://docs.python.org/3/reference/expressions.html#expression-lists
"""
def __init__(self, typ: TupleLyraType, items: List[Expression] = None):
"""Tuple construction.
:param typ: type of the tuple
:param items: list of items being displayed
"""
super().__init__(typ)
self._items = items or []
@property
def items(self):
return self._items
def __eq__(self, other: 'TupleDisplay'):
return (self.typ, self.items) == (other.typ, other.items)
def __hash__(self):
return hash((self.typ, str(self.items)))
def __str__(self):
str_items = map(str, self.items)
if len(self.items) == 1:
return f"({next(str_items)},)" # add a trailing comma
return '(' + ', '.join(str_items) + ')'
[docs]class SetDisplay(Expression):
"""Set display representation.
https://docs.python.org/3/reference/expressions.html#set-displays
"""
def __init__(self, typ: SetLyraType, items: List[Expression] = None):
"""Set display construction.
:param typ: type of the set
:param items: list of items being displayed
"""
super().__init__(typ)
self._items = items or []
@property
def items(self):
return self._items
def __eq__(self, other: 'SetDisplay'):
return (self.typ, self.items) == (other.typ, other.items)
def __hash__(self):
return hash((self.typ, str(self.items)))
def __str__(self):
str_items = map(str, self.items)
return '{' + ', '.join(str_items) + '}'
[docs]class DictDisplay(Expression):
"""Dictionary display representation.
https://docs.python.org/3/reference/expressions.html#dictionary-displays
"""
def __init__(self, typ: DictLyraType, keys: List[Expression] = None,
values: List[Expression] = None):
"""Dictionary display construction.
:param typ: type of the dictionary
:param keys, values: list of items being displayed (in the form key:value)
"""
super().__init__(typ)
self._keys = keys or []
self._values = values or []
@property
def keys(self):
return self._keys
@property
def values(self):
return self._values
def __eq__(self, other: 'DictDisplay'):
return (self.typ, self.keys, self.values) == (other.typ, other.keys, other.values)
def __hash__(self):
return hash((self.typ, str(self.keys), str(self.values)))
def __str__(self):
str_keys = map(str, self.keys)
str_values = map(str, self.values)
return '{' + ', '.join(' : '.join(x) for x in zip(str_keys, str_values)) + '}'
"""
Primary Expressions
https://docs.python.org/3.4/reference/expressions.html#primaries
"""
[docs]class AttributeReference(Expression):
"""Attribute reference representation.
https://docs.python.org/3.4/reference/expressions.html#attribute-references
"""
def __init__(self, typ: LyraType, target: Expression, attribute: Identifier):
"""Attribute reference construction.
:param typ: type of the attribute
:param target: object the attribute of which is being referenced
:param attribute: attribute being referenced
"""
super().__init__(typ)
self._target = target
self._attribute = attribute
@property
def target(self):
return self._target
@property
def attribute(self):
return self._attribute
def __eq__(self, other: 'AttributeReference'):
typ = self.typ == other.typ
target = self.target == other.target
attribute = self.attribute == other.attribute
return typ and target and attribute
def __hash__(self):
return hash((self.typ, self.target, self.attribute))
def __str__(self):
return "{0.target}.{0.attribute}".format(self)
[docs]class Subscription(Expression):
"""Subscription representation.
https://docs.python.org/3.4/reference/expressions.html#subscriptions
"""
def __init__(self, typ: LyraType, target: Expression, key: Expression):
"""Subscription construction.
:param typ: type of the subscription
:param target: object being subject to subscription
:param key: index at which the object is subscripted
"""
super().__init__(typ)
self._target = target
self._key = key
@property
def target(self):
return self._target
@property
def key(self):
return self._key
def __eq__(self, other: 'Subscription'):
typ = self.typ == other.typ
target = self.target == other.target
key = self.key == other.key
return typ and target and key
def __hash__(self):
return hash((self.typ, self.target, self.key))
def __str__(self):
return "{0.target}[{0.key}]".format(self)
[docs]class Slicing(Expression):
"""Slicing representation.
https://docs.python.org/3.4/reference/expressions.html#slicings
"""
def __init__(self, typ: LyraType, target: Expression,
lower: Expression, upper: Expression, stride: Expression = None):
"""Slicing construction.
:param typ: type of the slicing
:param target: object being subject to slicing
:param lower: lower bound of the slicing
:param upper: upper bound of the slicing
:param stride: stride of the slicing
"""
super().__init__(typ)
self._target = target
self._lower = lower
self._upper = upper
self._stride = stride
@property
def target(self):
return self._target
@property
def lower(self):
return self._lower
@property
def upper(self):
return self._upper
@property
def stride(self):
return self._stride
def __eq__(self, other: 'Slicing'):
typ = self.typ == other.typ
target = self.target == other.target
lower = self.lower == other.lower
upper = self.upper == other.upper
stride = self.stride == other.stride
return typ and target and lower and upper and stride
def __hash__(self):
return hash((self.typ, self.target, self.lower, self.upper, self.stride))
def __str__(self):
if self.stride:
return "{0.target}[{0.lower}:{0.upper}:{0.stride}]".format(self)
return "{0.target}[{0.lower}:{0.upper}]".format(self)
[docs]class Call(Expression, metaclass=ABCMeta):
"""Call representation.
https://docs.python.org/3.4/reference/expressions.html#calls
"""
[docs]class Range(Call):
"""Range call representation."""
def __init__(self, typ: LyraType, start: Expression, stop: Expression, step: Expression):
"""Range call construction.
:param typ: return type of the range call
:param start: start of the range sequence
:param stop: end of the range sequence (exclusive)
:param step: difference between elements of the sequence
"""
super().__init__(typ)
self._start = start
self._stop = stop
self._step = step
@property
def start(self):
return self._start
@property
def stop(self):
return self._stop
@property
def step(self):
return self._step
def __eq__(self, other: 'Range'):
typ = self.typ == other.typ
start = self.start == other.start
stop = self.stop == other.stop
step = self.step == other.step
return typ and start and stop and step
def __hash__(self):
return hash((self.typ, self.start, self.stop, self.step))
def __str__(self):
return f"range({self.start}, {self.stop}, {self.step})"
[docs]class Items(Call):
"""Items call representation"""
def __init__(self, typ: LyraType, target_dict: Expression):
"""Items() call expression construction.
:param typ: type that items() returns
:param target_dict: target of the items() call
"""
super().__init__(typ)
self._target_dict = target_dict
@property
def target_dict(self):
return self._target_dict
def __eq__(self, other: 'Items'):
return (self.typ == other.typ) and (self.target_dict == other.target_dict)
def __hash__(self):
return hash((self.typ, str(self.target_dict)))
def __str__(self):
return f"{self.target_dict}.items()"
[docs]class Keys(Call):
"""Keys call representation"""
def __init__(self, typ: LyraType, target_dict: Expression):
"""Keys() call expression construction.
:param typ: type that keys() returns
:param target_dict: target of the keys() call
"""
super().__init__(typ)
self._target_dict = target_dict
@property
def target_dict(self):
return self._target_dict
def __eq__(self, other: 'Keys'):
return (self.typ == other.typ) and (self.target_dict == other.target_dict)
def __hash__(self):
return hash((self.typ, str(self.target_dict)))
def __str__(self):
return f"{self.target_dict}.keys()"
[docs]class Values(Call):
"""Values call representation"""
def __init__(self, typ: LyraType, target_dict: Expression):
"""Values() call expression construction.
:param typ: type that values() returns
:param target_dict: target of the values() call
"""
super().__init__(typ)
self._target_dict = target_dict
@property
def target_dict(self):
return self._target_dict
def __eq__(self, other: 'Values'):
return (self.typ == other.typ) and (self.target_dict == other.target_dict)
def __hash__(self):
return hash((self.typ, str(self.target_dict)))
def __str__(self):
return f"{self.target_dict}.values()"
"""
Operation Expressions
"""
[docs]class Operation(Expression, metaclass=ABCMeta):
"""Operation representation."""
"""
Unary Operation Expressions
"""
[docs]class UnaryOperation(Operation):
"""Unary operation representation."""
[docs] class Operator(IntEnum):
"""Unary operator representation."""
@abstractmethod
def __str__(self):
"""Unary operator string representation.
:return: string representing the operator
"""
def __init__(self, typ: LyraType, operator: Operator, expression: Expression):
"""Unary operation construction.
:param typ: type of the operation
:param operator: operator of the operation
:param expression: expression of the operation
"""
super().__init__(typ)
self._operator = operator
self._expression = expression
@property
def operator(self):
return self._operator
@property
def expression(self):
return self._expression
def __eq__(self, other: 'UnaryOperation'):
typ = self.typ == other.typ
operator = self.operator == other.operator
expression = self.expression == other.expression
return typ and operator and expression
def __hash__(self):
return hash((self.typ, self.operator, self.expression))
def __str__(self):
expr_string = str(self.expression)
if isinstance(self.expression, Operation):
expr_string = f"({expr_string})"
return f"{str(self.operator)}{expr_string}"
[docs]class UnaryArithmeticOperation(UnaryOperation):
"""Unary arithmetic operation expression representation.
https://docs.python.org/3.4/reference/expressions.html#unary-arithmetic-and-bitwise-operations
"""
[docs] class Operator(UnaryOperation.Operator):
"""Unary arithmetic operator representation."""
Add = 1
Sub = -1
def __str__(self):
if self.value == 1:
return "+"
elif self.value == -1:
return "-"
def __init__(self, typ: LyraType, operator: Operator, expression: Expression):
"""Unary arithmetic operation expression representation.
:param typ: type of the operation
:param operator: operator of the operation
:param expression: expression of the operation
"""
super().__init__(typ, operator, expression)
[docs]class UnaryBooleanOperation(UnaryOperation):
"""Unary boolean operation expression representation.
https://docs.python.org/3.4/reference/expressions.html#boolean-operations
"""
[docs] class Operator(UnaryOperation.Operator):
"""Unary boolean operator representation."""
Neg = 1
def __str__(self):
if self.value == 1:
return "not"
def __init__(self, typ: LyraType, operator: Operator, expression: Expression):
"""Unary boolean operation expression representation.
:param typ: type of the operation
:param operator: operator of the operation
:param expression: expression of the operation
"""
super().__init__(typ, operator, expression)
"""
Binary Operation Expressions
"""
[docs]class BinaryOperation(Operation):
"""Binary operation representation."""
[docs] class Operator(IntEnum):
"""Binary operator representation."""
@abstractmethod
def __str__(self):
"""Binary operator string representation.
:return: string representing the operator
"""
def __init__(self, typ: LyraType, left: Expression, operator: Operator, right: Expression):
"""Binary operation construction.
:param typ: type of the operation
:param left: left expression of the operation
:param operator: operator of the operation
:param right: right expression of the operation
"""
super().__init__(typ)
self._left = left
self._operator = operator
self._right = right
@property
def left(self):
return self._left
@property
def operator(self):
return self._operator
@property
def right(self):
return self._right
def __eq__(self, other: 'BinaryOperation'):
typ = self.typ == other.typ
left = self.left == other.left
operator = self.operator == other.operator
right = self.right == other.right
return typ and left and operator and right
def __hash__(self):
return hash((self.typ, self.left, self.operator, self.right))
def __str__(self):
left_string = str(self.left)
right_string = str(self.right)
if isinstance(self.left, Operation):
left_string = f"({left_string})"
if isinstance(self.right, Operation):
right_string = f"({right_string})"
return f"{left_string} {str(self.operator)} {right_string}"
[docs]class BinaryArithmeticOperation(BinaryOperation):
"""Binary arithmetic operation expression representation.
https://docs.python.org/3.4/reference/expressions.html#binary-arithmetic-operations
"""
[docs] class Operator(BinaryOperation.Operator):
"""Binary arithmetic operator representation."""
Add = 1
Sub = 2
Mult = 3
Div = 4
def __str__(self):
if self.value == 1:
return "+"
elif self.value == 2:
return "-"
elif self.value == 3:
return "*"
elif self.value == 4:
return "/"
def __init__(self, typ: LyraType, left: Expression, operator: Operator, right: Expression):
"""Binary arithmetic operation expression representation.
:param typ: type of the operation
:param left: left expression of the operation
:param operator: operator of the operation
:param right: right expression of the operation
"""
super().__init__(typ, left, operator, right)
[docs]class BinarySequenceOperation(BinaryOperation):
"""Binary sequence operation expression representation."""
[docs] class Operator(BinaryOperation.Operator):
"""Binary sequence operator representation."""
Concat = 1
def __str__(self):
if self.value == 1:
return "+"
def __init__(self, typ: LyraType, left: Expression, operator: Operator, right: Expression):
"""Binary sequence operation expression representation.
:param typ: type of the operation
:param left: left expression of the operation
:param operator: operator of the operation
:param right: right expression of the operation
"""
super().__init__(typ, left, operator, right)
[docs]class BinaryBooleanOperation(BinaryOperation):
"""Binary boolean operation expression representation.
https://docs.python.org/3.6/reference/expressions.html#boolean-operations
"""
[docs] class Operator(BinaryOperation.Operator):
"""Binary arithmetic operator representation."""
And = 1
Or = 2
[docs] def reverse_operator(self):
"""Returns the reverse operator of this operator."""
if self.value == 1:
return BinaryBooleanOperation.Operator.Or
elif self.value == 2:
return BinaryBooleanOperation.Operator.And
def __str__(self):
return self.name.lower()
def __init__(self, typ: LyraType, left: Expression, operator: Operator, right: Expression):
"""Binary boolean operation expression representation.
:param typ: type of the operation
:param left: left expression of the operation
:param operator: operator of the operation
:param right: right expression of the operation
"""
super().__init__(typ, left, operator, right)
[docs]class BinaryComparisonOperation(BinaryOperation):
"""Binary comparison operation expression representation.
https://docs.python.org/3.4/reference/expressions.html#comparisons
"""
[docs] class Operator(BinaryOperation.Operator):
"""Binary comparison operator representation"""
Eq = 1
NotEq = 2
Lt = 3
LtE = 4
Gt = 5
GtE = 6
Is = 7
IsNot = 8
In = 9
NotIn = 10
[docs] def reverse_operator(self):
"""Returns the reverse operator of this operator."""
if self.value == 1:
return BinaryComparisonOperation.Operator.NotEq
elif self.value == 2:
return BinaryComparisonOperation.Operator.Eq
elif self.value == 3:
return BinaryComparisonOperation.Operator.GtE
elif self.value == 4:
return BinaryComparisonOperation.Operator.Gt
elif self.value == 5:
return BinaryComparisonOperation.Operator.LtE
elif self.value == 6:
return BinaryComparisonOperation.Operator.Lt
elif self.value == 7:
return BinaryComparisonOperation.Operator.IsNot
elif self.value == 8:
return BinaryComparisonOperation.Operator.Is
elif self.value == 9:
return BinaryComparisonOperation.Operator.NotIn
elif self.value == 10:
return BinaryComparisonOperation.Operator.In
def __str__(self):
if self.value == 1:
return "=="
elif self.value == 2:
return "!="
elif self.value == 3:
return "<"
elif self.value == 4:
return "<="
elif self.value == 5:
return ">"
elif self.value == 6:
return ">="
elif self.value == 7:
return "is"
elif self.value == 8:
return "is not"
elif self.value == 9:
return "in"
elif self.value == 10:
return "not in"
def __init__(self, typ: LyraType, left: Expression, operator: Operator, right: Expression):
"""Binary comparison operation expression representation.
:param typ: type of the operation
:param left: left expression of the operation
:param operator: operator of the operation
:param right: right expression of the operation
"""
super().__init__(typ, left, operator, right)