diff --git a/grammar/Expr.g4 b/grammar/Expr.g4 index 73e4d0ba..4249ded2 100644 --- a/grammar/Expr.g4 +++ b/grammar/Expr.g4 @@ -33,6 +33,7 @@ expr | IDENTIFIER '[' expr ']' # timeIndex | '(' expr ')' '[' shift ']' # timeShiftExpr | '(' expr ')' '[' expr ']' # timeIndexExpr + | MAX '(' expr (',' expr)* ')' # maxExpr ; atom @@ -74,6 +75,7 @@ fragment CHAR : [a-zA-Z_]; fragment CHAR_OR_DIGIT : (CHAR | DIGIT); NUMBER : DIGIT+ ('.' DIGIT+)?; +MAX : 'max'; TIME : 't'; IDENTIFIER : CHAR CHAR_OR_DIGIT*; COMPARISON : ( '=' | '>=' | '<=' ); diff --git a/src/gems/expression/__init__.py b/src/gems/expression/__init__.py index 3d949b6e..d2166af1 100644 --- a/src/gems/expression/__init__.py +++ b/src/gems/expression/__init__.py @@ -29,6 +29,9 @@ NegationNode, ParameterNode, VariableNode, + MaxNode, + TimeShiftNode, + max_expr, literal, param, sum_expressions, diff --git a/src/gems/expression/degree.py b/src/gems/expression/degree.py index d9051818..52b55dc0 100644 --- a/src/gems/expression/degree.py +++ b/src/gems/expression/degree.py @@ -22,6 +22,7 @@ TimeEvalNode, TimeShiftNode, TimeSumNode, + MaxNode, ) from .expression import ( @@ -52,8 +53,7 @@ def negation(self, node: NegationNode) -> int: # TODO: Take into account simplification that can occur with literal coefficient for add, sub, mult, div def addition(self, node: AdditionNode) -> int: - degrees = [visit(o, self) for o in node.operands] - return max(degrees) + return max([visit(o, self) for o in node.operands]) def multiplication(self, node: MultiplicationNode) -> int: return visit(node.left, self) + visit(node.right, self) @@ -108,6 +108,9 @@ def port_field(self, node: PortFieldNode) -> int: def port_field_aggregator(self, node: PortFieldAggregatorNode) -> int: return visit(node.operand, self) + def max_node(self, node: MaxNode) -> int: + return max([visit(o, self) for o in node.operands]) + def compute_degree(expression: ExpressionNode) -> int: return visit(expression, ExpressionDegreeVisitor()) diff --git a/src/gems/expression/equality.py b/src/gems/expression/equality.py index d5a543ff..d7ce4e0e 100644 --- a/src/gems/expression/equality.py +++ b/src/gems/expression/equality.py @@ -24,6 +24,7 @@ NegationNode, ParameterNode, VariableNode, + MaxNode, ) from gems.expression.expression import ( AllTimeSumNode, @@ -111,6 +112,8 @@ def visit(self, left: ExpressionNode, right: ExpressionNode) -> bool: right, PortFieldAggregatorNode ): return self.port_field_aggregator(left, right) + if isinstance(left, MaxNode) and isinstance(right, MaxNode): + return self.max_node(left, right) raise NotImplementedError(f"Equality not implemented for {left.__class__}") def literal(self, left: LiteralNode, right: LiteralNode) -> bool: @@ -133,6 +136,13 @@ def addition(self, left: AdditionNode, right: AdditionNode) -> bool: self.visit(l, r) for l, r in zip(left_ops, right_ops) ) + def max_node(self, left: MaxNode, right: MaxNode) -> bool: + if len(left.operands) != len(right.operands): + return False + return all( + self.visit(l_op, r_op) for l_op, r_op in zip(left.operands, right.operands) + ) + def multiplication( self, left: MultiplicationNode, right: MultiplicationNode ) -> bool: diff --git a/src/gems/expression/evaluate.py b/src/gems/expression/evaluate.py index 23c3cf4d..4614d5e1 100644 --- a/src/gems/expression/evaluate.py +++ b/src/gems/expression/evaluate.py @@ -18,6 +18,7 @@ AllTimeSumNode, ComponentParameterNode, ComponentVariableNode, + MaxNode, PortFieldAggregatorNode, PortFieldNode, ProblemParameterNode, @@ -139,6 +140,9 @@ def port_field(self, node: PortFieldNode) -> float: def port_field_aggregator(self, node: PortFieldAggregatorNode) -> float: raise NotImplementedError() + def max_node(self, node: MaxNode) -> float: # type: ignore + return max(visit(op, self) for op in node.operands) + def evaluate(expression: ExpressionNode, value_provider: ValueProvider) -> float: return visit(expression, EvaluationVisitor(value_provider)) diff --git a/src/gems/expression/expression.py b/src/gems/expression/expression.py index 44bfcfc9..d42af6ff 100644 --- a/src/gems/expression/expression.py +++ b/src/gems/expression/expression.py @@ -13,6 +13,7 @@ """ Defines the model for generic expressions. """ + import enum import inspect from dataclasses import dataclass @@ -150,6 +151,81 @@ def var(name: str) -> VariableNode: return VariableNode(name) +@dataclass(frozen=True, eq=False) +class MaxNode(ExpressionNode): + operands: List[ExpressionNode] + + def __post_init__(self) -> None: + if len(self.operands) < 2: + raise ValueError("MaxNode requires at least two operands") + for operand in self.operands: + if self._contains_forbidden_node(operand): + raise ValueError(f"Node {operand} is not allowed in this structure.") + + def _contains_forbidden_node(self, node: ExpressionNode) -> bool: + match node: + case ( + VariableNode() + | ComponentVariableNode() + | ProblemVariableNode() + | ComparisonNode() + | PortFieldNode() + | PortFieldAggregatorNode() + ): + return True + + case MaxNode(operands=ops): + return any(self._contains_forbidden_node(op) for op in ops) + + case TimeShiftNode(operand=op, time_shift=ts): + return self._contains_forbidden_node( + op + ) or self._contains_forbidden_node(ts) + + case AdditionNode(operands=ops): + return any(self._contains_forbidden_node(op) for op in ops) + + case DivisionNode(left=l, right=r) | MultiplicationNode(left=l, right=r): + return self._contains_forbidden_node( + l + ) or self._contains_forbidden_node(r) + + case ( + AllTimeSumNode(operand=op) + | NegationNode(operand=op) + | ScenarioOperatorNode(operand=op) + ): + return self._contains_forbidden_node(op) + + case TimeEvalNode(operand=op, eval_time=et): + return self._contains_forbidden_node( + op + ) or self._contains_forbidden_node(et) + + case TimeSumNode(operand=op, from_time=ft, to_time=tt): + return ( + self._contains_forbidden_node(op) + or self._contains_forbidden_node(ft) + or self._contains_forbidden_node(tt) + ) + + case ( + ComponentParameterNode() + | ProblemParameterNode() + | ParameterNode() + | LiteralNode() + ): + return False + + case _: + # By default for each new Node, we reject the "max" operation + return True + + +def max_expr(*operands: Any) -> MaxNode: + return MaxNode(operands=[_wrap_in_node(op) for op in operands]) + + @dataclass(frozen=True, eq=False) class PortFieldNode(ExpressionNode): """ diff --git a/src/gems/expression/indexing.py b/src/gems/expression/indexing.py index 022aa936..8c4f79b2 100644 --- a/src/gems/expression/indexing.py +++ b/src/gems/expression/indexing.py @@ -25,6 +25,7 @@ DivisionNode, ExpressionNode, LiteralNode, + MaxNode, MultiplicationNode, NegationNode, ParameterNode, @@ -159,6 +160,9 @@ def port_field_aggregator(self, node: PortFieldAggregatorNode) -> IndexingStruct "Port fields aggregators must be resolved before computing indexing structure." ) + def max_node(self, node: MaxNode) -> IndexingStructure: + return self._combine(node.operands) + def compute_indexation( expression: ExpressionNode, provider: IndexingStructureProvider diff --git a/src/gems/expression/parsing/antlr/Expr.interp b/src/gems/expression/parsing/antlr/Expr.interp index 94f1370f..e66a200f 100644 --- a/src/gems/expression/parsing/antlr/Expr.interp +++ b/src/gems/expression/parsing/antlr/Expr.interp @@ -14,6 +14,7 @@ null '[' ']' null +'max' 't' null null @@ -35,6 +36,7 @@ null null null NUMBER +MAX TIME IDENTIFIER COMPARISON @@ -51,4 +53,4 @@ right_expr atn: -[4, 1, 18, 140, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 79, 8, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 5, 2, 90, 8, 2, 10, 2, 12, 2, 93, 9, 2, 1, 3, 1, 3, 3, 3, 97, 8, 3, 1, 4, 1, 4, 3, 4, 101, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 111, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 119, 8, 5, 10, 5, 12, 5, 122, 9, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 130, 8, 6, 1, 6, 1, 6, 1, 6, 5, 6, 135, 8, 6, 10, 6, 12, 6, 138, 9, 6, 1, 6, 0, 3, 4, 10, 12, 7, 0, 2, 4, 6, 8, 10, 12, 0, 2, 1, 0, 5, 6, 2, 0, 2, 2, 7, 7, 153, 0, 14, 1, 0, 0, 0, 2, 18, 1, 0, 0, 0, 4, 78, 1, 0, 0, 0, 6, 96, 1, 0, 0, 0, 8, 98, 1, 0, 0, 0, 10, 110, 1, 0, 0, 0, 12, 129, 1, 0, 0, 0, 14, 15, 5, 16, 0, 0, 15, 16, 5, 1, 0, 0, 16, 17, 5, 16, 0, 0, 17, 1, 1, 0, 0, 0, 18, 19, 3, 4, 2, 0, 19, 20, 5, 0, 0, 1, 20, 3, 1, 0, 0, 0, 21, 22, 6, 2, -1, 0, 22, 79, 3, 6, 3, 0, 23, 79, 3, 0, 0, 0, 24, 25, 5, 2, 0, 0, 25, 79, 3, 4, 2, 13, 26, 27, 5, 3, 0, 0, 27, 28, 3, 4, 2, 0, 28, 29, 5, 4, 0, 0, 29, 79, 1, 0, 0, 0, 30, 31, 5, 8, 0, 0, 31, 32, 5, 3, 0, 0, 32, 33, 3, 4, 2, 0, 33, 34, 5, 4, 0, 0, 34, 79, 1, 0, 0, 0, 35, 36, 5, 9, 0, 0, 36, 37, 5, 3, 0, 0, 37, 38, 3, 0, 0, 0, 38, 39, 5, 4, 0, 0, 39, 79, 1, 0, 0, 0, 40, 41, 5, 8, 0, 0, 41, 42, 5, 3, 0, 0, 42, 43, 3, 8, 4, 0, 43, 44, 5, 10, 0, 0, 44, 45, 3, 8, 4, 0, 45, 46, 5, 11, 0, 0, 46, 47, 3, 4, 2, 0, 47, 48, 5, 4, 0, 0, 48, 79, 1, 0, 0, 0, 49, 50, 5, 16, 0, 0, 50, 51, 5, 3, 0, 0, 51, 52, 3, 4, 2, 0, 52, 53, 5, 4, 0, 0, 53, 79, 1, 0, 0, 0, 54, 55, 5, 16, 0, 0, 55, 56, 5, 12, 0, 0, 56, 57, 3, 8, 4, 0, 57, 58, 5, 13, 0, 0, 58, 79, 1, 0, 0, 0, 59, 60, 5, 16, 0, 0, 60, 61, 5, 12, 0, 0, 61, 62, 3, 4, 2, 0, 62, 63, 5, 13, 0, 0, 63, 79, 1, 0, 0, 0, 64, 65, 5, 3, 0, 0, 65, 66, 3, 4, 2, 0, 66, 67, 5, 4, 0, 0, 67, 68, 5, 12, 0, 0, 68, 69, 3, 8, 4, 0, 69, 70, 5, 13, 0, 0, 70, 79, 1, 0, 0, 0, 71, 72, 5, 3, 0, 0, 72, 73, 3, 4, 2, 0, 73, 74, 5, 4, 0, 0, 74, 75, 5, 12, 0, 0, 75, 76, 3, 4, 2, 0, 76, 77, 5, 13, 0, 0, 77, 79, 1, 0, 0, 0, 78, 21, 1, 0, 0, 0, 78, 23, 1, 0, 0, 0, 78, 24, 1, 0, 0, 0, 78, 26, 1, 0, 0, 0, 78, 30, 1, 0, 0, 0, 78, 35, 1, 0, 0, 0, 78, 40, 1, 0, 0, 0, 78, 49, 1, 0, 0, 0, 78, 54, 1, 0, 0, 0, 78, 59, 1, 0, 0, 0, 78, 64, 1, 0, 0, 0, 78, 71, 1, 0, 0, 0, 79, 91, 1, 0, 0, 0, 80, 81, 10, 11, 0, 0, 81, 82, 7, 0, 0, 0, 82, 90, 3, 4, 2, 12, 83, 84, 10, 10, 0, 0, 84, 85, 7, 1, 0, 0, 85, 90, 3, 4, 2, 11, 86, 87, 10, 9, 0, 0, 87, 88, 5, 17, 0, 0, 88, 90, 3, 4, 2, 10, 89, 80, 1, 0, 0, 0, 89, 83, 1, 0, 0, 0, 89, 86, 1, 0, 0, 0, 90, 93, 1, 0, 0, 0, 91, 89, 1, 0, 0, 0, 91, 92, 1, 0, 0, 0, 92, 5, 1, 0, 0, 0, 93, 91, 1, 0, 0, 0, 94, 97, 5, 14, 0, 0, 95, 97, 5, 16, 0, 0, 96, 94, 1, 0, 0, 0, 96, 95, 1, 0, 0, 0, 97, 7, 1, 0, 0, 0, 98, 100, 5, 15, 0, 0, 99, 101, 3, 10, 5, 0, 100, 99, 1, 0, 0, 0, 100, 101, 1, 0, 0, 0, 101, 9, 1, 0, 0, 0, 102, 103, 6, 5, -1, 0, 103, 104, 7, 1, 0, 0, 104, 111, 3, 6, 3, 0, 105, 106, 7, 1, 0, 0, 106, 107, 5, 3, 0, 0, 107, 108, 3, 4, 2, 0, 108, 109, 5, 4, 0, 0, 109, 111, 1, 0, 0, 0, 110, 102, 1, 0, 0, 0, 110, 105, 1, 0, 0, 0, 111, 120, 1, 0, 0, 0, 112, 113, 10, 4, 0, 0, 113, 114, 7, 0, 0, 0, 114, 119, 3, 12, 6, 0, 115, 116, 10, 3, 0, 0, 116, 117, 7, 1, 0, 0, 117, 119, 3, 12, 6, 0, 118, 112, 1, 0, 0, 0, 118, 115, 1, 0, 0, 0, 119, 122, 1, 0, 0, 0, 120, 118, 1, 0, 0, 0, 120, 121, 1, 0, 0, 0, 121, 11, 1, 0, 0, 0, 122, 120, 1, 0, 0, 0, 123, 124, 6, 6, -1, 0, 124, 125, 5, 3, 0, 0, 125, 126, 3, 4, 2, 0, 126, 127, 5, 4, 0, 0, 127, 130, 1, 0, 0, 0, 128, 130, 3, 6, 3, 0, 129, 123, 1, 0, 0, 0, 129, 128, 1, 0, 0, 0, 130, 136, 1, 0, 0, 0, 131, 132, 10, 3, 0, 0, 132, 133, 7, 0, 0, 0, 133, 135, 3, 12, 6, 4, 134, 131, 1, 0, 0, 0, 135, 138, 1, 0, 0, 0, 136, 134, 1, 0, 0, 0, 136, 137, 1, 0, 0, 0, 137, 13, 1, 0, 0, 0, 138, 136, 1, 0, 0, 0, 10, 78, 89, 91, 96, 100, 110, 118, 120, 129, 136] \ No newline at end of file +[4, 1, 19, 152, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 5, 2, 84, 8, 2, 10, 2, 12, 2, 87, 9, 2, 1, 2, 1, 2, 3, 2, 91, 8, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 5, 2, 102, 8, 2, 10, 2, 12, 2, 105, 9, 2, 1, 3, 1, 3, 3, 3, 109, 8, 3, 1, 4, 1, 4, 3, 4, 113, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 123, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 131, 8, 5, 10, 5, 12, 5, 134, 9, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 142, 8, 6, 1, 6, 1, 6, 1, 6, 5, 6, 147, 8, 6, 10, 6, 12, 6, 150, 9, 6, 1, 6, 0, 3, 4, 10, 12, 7, 0, 2, 4, 6, 8, 10, 12, 0, 2, 1, 0, 5, 6, 2, 0, 2, 2, 7, 7, 167, 0, 14, 1, 0, 0, 0, 2, 18, 1, 0, 0, 0, 4, 90, 1, 0, 0, 0, 6, 108, 1, 0, 0, 0, 8, 110, 1, 0, 0, 0, 10, 122, 1, 0, 0, 0, 12, 141, 1, 0, 0, 0, 14, 15, 5, 17, 0, 0, 15, 16, 5, 1, 0, 0, 16, 17, 5, 17, 0, 0, 17, 1, 1, 0, 0, 0, 18, 19, 3, 4, 2, 0, 19, 20, 5, 0, 0, 1, 20, 3, 1, 0, 0, 0, 21, 22, 6, 2, -1, 0, 22, 91, 3, 6, 3, 0, 23, 91, 3, 0, 0, 0, 24, 25, 5, 2, 0, 0, 25, 91, 3, 4, 2, 14, 26, 27, 5, 3, 0, 0, 27, 28, 3, 4, 2, 0, 28, 29, 5, 4, 0, 0, 29, 91, 1, 0, 0, 0, 30, 31, 5, 8, 0, 0, 31, 32, 5, 3, 0, 0, 32, 33, 3, 4, 2, 0, 33, 34, 5, 4, 0, 0, 34, 91, 1, 0, 0, 0, 35, 36, 5, 9, 0, 0, 36, 37, 5, 3, 0, 0, 37, 38, 3, 0, 0, 0, 38, 39, 5, 4, 0, 0, 39, 91, 1, 0, 0, 0, 40, 41, 5, 8, 0, 0, 41, 42, 5, 3, 0, 0, 42, 43, 3, 8, 4, 0, 43, 44, 5, 10, 0, 0, 44, 45, 3, 8, 4, 0, 45, 46, 5, 11, 0, 0, 46, 47, 3, 4, 2, 0, 47, 48, 5, 4, 0, 0, 48, 91, 1, 0, 0, 0, 49, 50, 5, 17, 0, 0, 50, 51, 5, 3, 0, 0, 51, 52, 3, 4, 2, 0, 52, 53, 5, 4, 0, 0, 53, 91, 1, 0, 0, 0, 54, 55, 5, 17, 0, 0, 55, 56, 5, 12, 0, 0, 56, 57, 3, 8, 4, 0, 57, 58, 5, 13, 0, 0, 58, 91, 1, 0, 0, 0, 59, 60, 5, 17, 0, 0, 60, 61, 5, 12, 0, 0, 61, 62, 3, 4, 2, 0, 62, 63, 5, 13, 0, 0, 63, 91, 1, 0, 0, 0, 64, 65, 5, 3, 0, 0, 65, 66, 3, 4, 2, 0, 66, 67, 5, 4, 0, 0, 67, 68, 5, 12, 0, 0, 68, 69, 3, 8, 4, 0, 69, 70, 5, 13, 0, 0, 70, 91, 1, 0, 0, 0, 71, 72, 5, 3, 0, 0, 72, 73, 3, 4, 2, 0, 73, 74, 5, 4, 0, 0, 74, 75, 5, 12, 0, 0, 75, 76, 3, 4, 2, 0, 76, 77, 5, 13, 0, 0, 77, 91, 1, 0, 0, 0, 78, 79, 5, 15, 0, 0, 79, 80, 5, 3, 0, 0, 80, 85, 3, 4, 2, 0, 81, 82, 5, 11, 0, 0, 82, 84, 3, 4, 2, 0, 83, 81, 1, 0, 0, 0, 84, 87, 1, 0, 0, 0, 85, 83, 1, 0, 0, 0, 85, 86, 1, 0, 0, 0, 86, 88, 1, 0, 0, 0, 87, 85, 1, 0, 0, 0, 88, 89, 5, 4, 0, 0, 89, 91, 1, 0, 0, 0, 90, 21, 1, 0, 0, 0, 90, 23, 1, 0, 0, 0, 90, 24, 1, 0, 0, 0, 90, 26, 1, 0, 0, 0, 90, 30, 1, 0, 0, 0, 90, 35, 1, 0, 0, 0, 90, 40, 1, 0, 0, 0, 90, 49, 1, 0, 0, 0, 90, 54, 1, 0, 0, 0, 90, 59, 1, 0, 0, 0, 90, 64, 1, 0, 0, 0, 90, 71, 1, 0, 0, 0, 90, 78, 1, 0, 0, 0, 91, 103, 1, 0, 0, 0, 92, 93, 10, 12, 0, 0, 93, 94, 7, 0, 0, 0, 94, 102, 3, 4, 2, 13, 95, 96, 10, 11, 0, 0, 96, 97, 7, 1, 0, 0, 97, 102, 3, 4, 2, 12, 98, 99, 10, 10, 0, 0, 99, 100, 5, 18, 0, 0, 100, 102, 3, 4, 2, 11, 101, 92, 1, 0, 0, 0, 101, 95, 1, 0, 0, 0, 101, 98, 1, 0, 0, 0, 102, 105, 1, 0, 0, 0, 103, 101, 1, 0, 0, 0, 103, 104, 1, 0, 0, 0, 104, 5, 1, 0, 0, 0, 105, 103, 1, 0, 0, 0, 106, 109, 5, 14, 0, 0, 107, 109, 5, 17, 0, 0, 108, 106, 1, 0, 0, 0, 108, 107, 1, 0, 0, 0, 109, 7, 1, 0, 0, 0, 110, 112, 5, 16, 0, 0, 111, 113, 3, 10, 5, 0, 112, 111, 1, 0, 0, 0, 112, 113, 1, 0, 0, 0, 113, 9, 1, 0, 0, 0, 114, 115, 6, 5, -1, 0, 115, 116, 7, 1, 0, 0, 116, 123, 3, 6, 3, 0, 117, 118, 7, 1, 0, 0, 118, 119, 5, 3, 0, 0, 119, 120, 3, 4, 2, 0, 120, 121, 5, 4, 0, 0, 121, 123, 1, 0, 0, 0, 122, 114, 1, 0, 0, 0, 122, 117, 1, 0, 0, 0, 123, 132, 1, 0, 0, 0, 124, 125, 10, 4, 0, 0, 125, 126, 7, 0, 0, 0, 126, 131, 3, 12, 6, 0, 127, 128, 10, 3, 0, 0, 128, 129, 7, 1, 0, 0, 129, 131, 3, 12, 6, 0, 130, 124, 1, 0, 0, 0, 130, 127, 1, 0, 0, 0, 131, 134, 1, 0, 0, 0, 132, 130, 1, 0, 0, 0, 132, 133, 1, 0, 0, 0, 133, 11, 1, 0, 0, 0, 134, 132, 1, 0, 0, 0, 135, 136, 6, 6, -1, 0, 136, 137, 5, 3, 0, 0, 137, 138, 3, 4, 2, 0, 138, 139, 5, 4, 0, 0, 139, 142, 1, 0, 0, 0, 140, 142, 3, 6, 3, 0, 141, 135, 1, 0, 0, 0, 141, 140, 1, 0, 0, 0, 142, 148, 1, 0, 0, 0, 143, 144, 10, 3, 0, 0, 144, 145, 7, 0, 0, 0, 145, 147, 3, 12, 6, 4, 146, 143, 1, 0, 0, 0, 147, 150, 1, 0, 0, 0, 148, 146, 1, 0, 0, 0, 148, 149, 1, 0, 0, 0, 149, 13, 1, 0, 0, 0, 150, 148, 1, 0, 0, 0, 11, 85, 90, 101, 103, 108, 112, 122, 130, 132, 141, 148] \ No newline at end of file diff --git a/src/gems/expression/parsing/antlr/Expr.tokens b/src/gems/expression/parsing/antlr/Expr.tokens index c8638328..f367a876 100644 --- a/src/gems/expression/parsing/antlr/Expr.tokens +++ b/src/gems/expression/parsing/antlr/Expr.tokens @@ -12,10 +12,11 @@ T__10=11 T__11=12 T__12=13 NUMBER=14 -TIME=15 -IDENTIFIER=16 -COMPARISON=17 -WS=18 +MAX=15 +TIME=16 +IDENTIFIER=17 +COMPARISON=18 +WS=19 '.'=1 '-'=2 '('=3 @@ -29,4 +30,5 @@ WS=18 ','=11 '['=12 ']'=13 -'t'=15 +'max'=15 +'t'=16 diff --git a/src/gems/expression/parsing/antlr/ExprLexer.interp b/src/gems/expression/parsing/antlr/ExprLexer.interp index d761b1f1..8eaa90d8 100644 --- a/src/gems/expression/parsing/antlr/ExprLexer.interp +++ b/src/gems/expression/parsing/antlr/ExprLexer.interp @@ -14,6 +14,7 @@ null '[' ']' null +'max' 't' null null @@ -35,6 +36,7 @@ null null null NUMBER +MAX TIME IDENTIFIER COMPARISON @@ -58,6 +60,7 @@ DIGIT CHAR CHAR_OR_DIGIT NUMBER +MAX TIME IDENTIFIER COMPARISON @@ -71,4 +74,4 @@ mode names: DEFAULT_MODE atn: -[4, 0, 18, 127, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 3, 15, 93, 8, 15, 1, 16, 4, 16, 96, 8, 16, 11, 16, 12, 16, 97, 1, 16, 1, 16, 4, 16, 102, 8, 16, 11, 16, 12, 16, 103, 3, 16, 106, 8, 16, 1, 17, 1, 17, 1, 18, 1, 18, 5, 18, 112, 8, 18, 10, 18, 12, 18, 115, 9, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 3, 19, 122, 8, 19, 1, 20, 1, 20, 1, 20, 1, 20, 0, 0, 21, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 0, 29, 0, 31, 0, 33, 14, 35, 15, 37, 16, 39, 17, 41, 18, 1, 0, 3, 1, 0, 48, 57, 3, 0, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 130, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 1, 43, 1, 0, 0, 0, 3, 45, 1, 0, 0, 0, 5, 47, 1, 0, 0, 0, 7, 49, 1, 0, 0, 0, 9, 51, 1, 0, 0, 0, 11, 53, 1, 0, 0, 0, 13, 55, 1, 0, 0, 0, 15, 57, 1, 0, 0, 0, 17, 61, 1, 0, 0, 0, 19, 77, 1, 0, 0, 0, 21, 80, 1, 0, 0, 0, 23, 82, 1, 0, 0, 0, 25, 84, 1, 0, 0, 0, 27, 86, 1, 0, 0, 0, 29, 88, 1, 0, 0, 0, 31, 92, 1, 0, 0, 0, 33, 95, 1, 0, 0, 0, 35, 107, 1, 0, 0, 0, 37, 109, 1, 0, 0, 0, 39, 121, 1, 0, 0, 0, 41, 123, 1, 0, 0, 0, 43, 44, 5, 46, 0, 0, 44, 2, 1, 0, 0, 0, 45, 46, 5, 45, 0, 0, 46, 4, 1, 0, 0, 0, 47, 48, 5, 40, 0, 0, 48, 6, 1, 0, 0, 0, 49, 50, 5, 41, 0, 0, 50, 8, 1, 0, 0, 0, 51, 52, 5, 47, 0, 0, 52, 10, 1, 0, 0, 0, 53, 54, 5, 42, 0, 0, 54, 12, 1, 0, 0, 0, 55, 56, 5, 43, 0, 0, 56, 14, 1, 0, 0, 0, 57, 58, 5, 115, 0, 0, 58, 59, 5, 117, 0, 0, 59, 60, 5, 109, 0, 0, 60, 16, 1, 0, 0, 0, 61, 62, 5, 115, 0, 0, 62, 63, 5, 117, 0, 0, 63, 64, 5, 109, 0, 0, 64, 65, 5, 95, 0, 0, 65, 66, 5, 99, 0, 0, 66, 67, 5, 111, 0, 0, 67, 68, 5, 110, 0, 0, 68, 69, 5, 110, 0, 0, 69, 70, 5, 101, 0, 0, 70, 71, 5, 99, 0, 0, 71, 72, 5, 116, 0, 0, 72, 73, 5, 105, 0, 0, 73, 74, 5, 111, 0, 0, 74, 75, 5, 110, 0, 0, 75, 76, 5, 115, 0, 0, 76, 18, 1, 0, 0, 0, 77, 78, 5, 46, 0, 0, 78, 79, 5, 46, 0, 0, 79, 20, 1, 0, 0, 0, 80, 81, 5, 44, 0, 0, 81, 22, 1, 0, 0, 0, 82, 83, 5, 91, 0, 0, 83, 24, 1, 0, 0, 0, 84, 85, 5, 93, 0, 0, 85, 26, 1, 0, 0, 0, 86, 87, 7, 0, 0, 0, 87, 28, 1, 0, 0, 0, 88, 89, 7, 1, 0, 0, 89, 30, 1, 0, 0, 0, 90, 93, 3, 29, 14, 0, 91, 93, 3, 27, 13, 0, 92, 90, 1, 0, 0, 0, 92, 91, 1, 0, 0, 0, 93, 32, 1, 0, 0, 0, 94, 96, 3, 27, 13, 0, 95, 94, 1, 0, 0, 0, 96, 97, 1, 0, 0, 0, 97, 95, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 105, 1, 0, 0, 0, 99, 101, 5, 46, 0, 0, 100, 102, 3, 27, 13, 0, 101, 100, 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 103, 101, 1, 0, 0, 0, 103, 104, 1, 0, 0, 0, 104, 106, 1, 0, 0, 0, 105, 99, 1, 0, 0, 0, 105, 106, 1, 0, 0, 0, 106, 34, 1, 0, 0, 0, 107, 108, 5, 116, 0, 0, 108, 36, 1, 0, 0, 0, 109, 113, 3, 29, 14, 0, 110, 112, 3, 31, 15, 0, 111, 110, 1, 0, 0, 0, 112, 115, 1, 0, 0, 0, 113, 111, 1, 0, 0, 0, 113, 114, 1, 0, 0, 0, 114, 38, 1, 0, 0, 0, 115, 113, 1, 0, 0, 0, 116, 122, 5, 61, 0, 0, 117, 118, 5, 62, 0, 0, 118, 122, 5, 61, 0, 0, 119, 120, 5, 60, 0, 0, 120, 122, 5, 61, 0, 0, 121, 116, 1, 0, 0, 0, 121, 117, 1, 0, 0, 0, 121, 119, 1, 0, 0, 0, 122, 40, 1, 0, 0, 0, 123, 124, 7, 2, 0, 0, 124, 125, 1, 0, 0, 0, 125, 126, 6, 20, 0, 0, 126, 42, 1, 0, 0, 0, 7, 0, 92, 97, 103, 105, 113, 121, 1, 6, 0, 0] \ No newline at end of file +[4, 0, 19, 133, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 3, 15, 95, 8, 15, 1, 16, 4, 16, 98, 8, 16, 11, 16, 12, 16, 99, 1, 16, 1, 16, 4, 16, 104, 8, 16, 11, 16, 12, 16, 105, 3, 16, 108, 8, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 19, 1, 19, 5, 19, 118, 8, 19, 10, 19, 12, 19, 121, 9, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 128, 8, 20, 1, 21, 1, 21, 1, 21, 1, 21, 0, 0, 22, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 0, 29, 0, 31, 0, 33, 14, 35, 15, 37, 16, 39, 17, 41, 18, 43, 19, 1, 0, 3, 1, 0, 48, 57, 3, 0, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 136, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 1, 45, 1, 0, 0, 0, 3, 47, 1, 0, 0, 0, 5, 49, 1, 0, 0, 0, 7, 51, 1, 0, 0, 0, 9, 53, 1, 0, 0, 0, 11, 55, 1, 0, 0, 0, 13, 57, 1, 0, 0, 0, 15, 59, 1, 0, 0, 0, 17, 63, 1, 0, 0, 0, 19, 79, 1, 0, 0, 0, 21, 82, 1, 0, 0, 0, 23, 84, 1, 0, 0, 0, 25, 86, 1, 0, 0, 0, 27, 88, 1, 0, 0, 0, 29, 90, 1, 0, 0, 0, 31, 94, 1, 0, 0, 0, 33, 97, 1, 0, 0, 0, 35, 109, 1, 0, 0, 0, 37, 113, 1, 0, 0, 0, 39, 115, 1, 0, 0, 0, 41, 127, 1, 0, 0, 0, 43, 129, 1, 0, 0, 0, 45, 46, 5, 46, 0, 0, 46, 2, 1, 0, 0, 0, 47, 48, 5, 45, 0, 0, 48, 4, 1, 0, 0, 0, 49, 50, 5, 40, 0, 0, 50, 6, 1, 0, 0, 0, 51, 52, 5, 41, 0, 0, 52, 8, 1, 0, 0, 0, 53, 54, 5, 47, 0, 0, 54, 10, 1, 0, 0, 0, 55, 56, 5, 42, 0, 0, 56, 12, 1, 0, 0, 0, 57, 58, 5, 43, 0, 0, 58, 14, 1, 0, 0, 0, 59, 60, 5, 115, 0, 0, 60, 61, 5, 117, 0, 0, 61, 62, 5, 109, 0, 0, 62, 16, 1, 0, 0, 0, 63, 64, 5, 115, 0, 0, 64, 65, 5, 117, 0, 0, 65, 66, 5, 109, 0, 0, 66, 67, 5, 95, 0, 0, 67, 68, 5, 99, 0, 0, 68, 69, 5, 111, 0, 0, 69, 70, 5, 110, 0, 0, 70, 71, 5, 110, 0, 0, 71, 72, 5, 101, 0, 0, 72, 73, 5, 99, 0, 0, 73, 74, 5, 116, 0, 0, 74, 75, 5, 105, 0, 0, 75, 76, 5, 111, 0, 0, 76, 77, 5, 110, 0, 0, 77, 78, 5, 115, 0, 0, 78, 18, 1, 0, 0, 0, 79, 80, 5, 46, 0, 0, 80, 81, 5, 46, 0, 0, 81, 20, 1, 0, 0, 0, 82, 83, 5, 44, 0, 0, 83, 22, 1, 0, 0, 0, 84, 85, 5, 91, 0, 0, 85, 24, 1, 0, 0, 0, 86, 87, 5, 93, 0, 0, 87, 26, 1, 0, 0, 0, 88, 89, 7, 0, 0, 0, 89, 28, 1, 0, 0, 0, 90, 91, 7, 1, 0, 0, 91, 30, 1, 0, 0, 0, 92, 95, 3, 29, 14, 0, 93, 95, 3, 27, 13, 0, 94, 92, 1, 0, 0, 0, 94, 93, 1, 0, 0, 0, 95, 32, 1, 0, 0, 0, 96, 98, 3, 27, 13, 0, 97, 96, 1, 0, 0, 0, 98, 99, 1, 0, 0, 0, 99, 97, 1, 0, 0, 0, 99, 100, 1, 0, 0, 0, 100, 107, 1, 0, 0, 0, 101, 103, 5, 46, 0, 0, 102, 104, 3, 27, 13, 0, 103, 102, 1, 0, 0, 0, 104, 105, 1, 0, 0, 0, 105, 103, 1, 0, 0, 0, 105, 106, 1, 0, 0, 0, 106, 108, 1, 0, 0, 0, 107, 101, 1, 0, 0, 0, 107, 108, 1, 0, 0, 0, 108, 34, 1, 0, 0, 0, 109, 110, 5, 109, 0, 0, 110, 111, 5, 97, 0, 0, 111, 112, 5, 120, 0, 0, 112, 36, 1, 0, 0, 0, 113, 114, 5, 116, 0, 0, 114, 38, 1, 0, 0, 0, 115, 119, 3, 29, 14, 0, 116, 118, 3, 31, 15, 0, 117, 116, 1, 0, 0, 0, 118, 121, 1, 0, 0, 0, 119, 117, 1, 0, 0, 0, 119, 120, 1, 0, 0, 0, 120, 40, 1, 0, 0, 0, 121, 119, 1, 0, 0, 0, 122, 128, 5, 61, 0, 0, 123, 124, 5, 62, 0, 0, 124, 128, 5, 61, 0, 0, 125, 126, 5, 60, 0, 0, 126, 128, 5, 61, 0, 0, 127, 122, 1, 0, 0, 0, 127, 123, 1, 0, 0, 0, 127, 125, 1, 0, 0, 0, 128, 42, 1, 0, 0, 0, 129, 130, 7, 2, 0, 0, 130, 131, 1, 0, 0, 0, 131, 132, 6, 21, 0, 0, 132, 44, 1, 0, 0, 0, 7, 0, 94, 99, 105, 107, 119, 127, 1, 6, 0, 0] \ No newline at end of file diff --git a/src/gems/expression/parsing/antlr/ExprLexer.py b/src/gems/expression/parsing/antlr/ExprLexer.py index 60c2d135..86bdaa43 100644 --- a/src/gems/expression/parsing/antlr/ExprLexer.py +++ b/src/gems/expression/parsing/antlr/ExprLexer.py @@ -1,8 +1,7 @@ # Generated from Expr.g4 by ANTLR 4.13.2 -import sys -from io import StringIO - from antlr4 import * +from io import StringIO +import sys if sys.version_info[1] > 5: from typing import TextIO @@ -14,8 +13,8 @@ def serializedATN(): return [ 4, 0, - 18, - 127, + 19, + 133, 6, -1, 2, @@ -102,6 +101,10 @@ def serializedATN(): 20, 7, 20, + 2, + 21, + 7, + 21, 1, 0, 1, @@ -202,38 +205,38 @@ def serializedATN(): 15, 3, 15, - 93, + 95, 8, 15, 1, 16, 4, 16, - 96, + 98, 8, 16, 11, 16, 12, 16, - 97, + 99, 1, 16, 1, 16, 4, 16, - 102, + 104, 8, 16, 11, 16, 12, 16, - 103, + 105, 3, 16, - 106, + 108, 8, 16, 1, @@ -241,35 +244,28 @@ def serializedATN(): 1, 17, 1, - 18, + 17, + 1, + 17, 1, 18, - 5, - 18, - 112, - 8, - 18, - 10, - 18, - 12, - 18, - 115, - 9, + 1, 18, 1, 19, 1, 19, - 1, + 5, 19, - 1, + 118, + 8, 19, - 1, + 10, 19, - 3, + 12, 19, - 122, - 8, + 121, + 9, 19, 1, 20, @@ -279,9 +275,24 @@ def serializedATN(): 20, 1, 20, + 1, + 20, + 3, + 20, + 128, + 8, + 20, + 1, + 21, + 1, + 21, + 1, + 21, + 1, + 21, 0, 0, - 21, + 22, 1, 1, 3, @@ -324,6 +335,8 @@ def serializedATN(): 17, 41, 18, + 43, + 19, 1, 0, 3, @@ -347,7 +360,7 @@ def serializedATN(): 13, 32, 32, - 130, + 136, 0, 1, 1, @@ -456,140 +469,140 @@ def serializedATN(): 0, 0, 0, - 1, + 0, 43, 1, 0, 0, 0, - 3, + 1, 45, 1, 0, 0, 0, - 5, + 3, 47, 1, 0, 0, 0, - 7, + 5, 49, 1, 0, 0, 0, - 9, + 7, 51, 1, 0, 0, 0, - 11, + 9, 53, 1, 0, 0, 0, - 13, + 11, 55, 1, 0, 0, 0, - 15, + 13, 57, 1, 0, 0, 0, + 15, + 59, + 1, + 0, + 0, + 0, 17, - 61, + 63, 1, 0, 0, 0, 19, - 77, + 79, 1, 0, 0, 0, 21, - 80, + 82, 1, 0, 0, 0, 23, - 82, + 84, 1, 0, 0, 0, 25, - 84, + 86, 1, 0, 0, 0, 27, - 86, + 88, 1, 0, 0, 0, 29, - 88, + 90, 1, 0, 0, 0, 31, - 92, + 94, 1, 0, 0, 0, 33, - 95, + 97, 1, 0, 0, 0, 35, - 107, + 109, 1, 0, 0, 0, 37, - 109, + 113, 1, 0, 0, 0, 39, - 121, + 115, 1, 0, 0, 0, 41, - 123, + 127, 1, 0, 0, 0, 43, - 44, - 5, - 46, - 0, - 0, - 44, - 2, + 129, 1, 0, 0, @@ -597,11 +610,11 @@ def serializedATN(): 45, 46, 5, - 45, + 46, 0, 0, 46, - 4, + 2, 1, 0, 0, @@ -609,11 +622,11 @@ def serializedATN(): 47, 48, 5, - 40, + 45, 0, 0, 48, - 6, + 4, 1, 0, 0, @@ -621,11 +634,11 @@ def serializedATN(): 49, 50, 5, - 41, + 40, 0, 0, 50, - 8, + 6, 1, 0, 0, @@ -633,11 +646,11 @@ def serializedATN(): 51, 52, 5, - 47, + 41, 0, 0, 52, - 10, + 8, 1, 0, 0, @@ -645,11 +658,11 @@ def serializedATN(): 53, 54, 5, - 42, + 47, 0, 0, 54, - 12, + 10, 1, 0, 0, @@ -657,11 +670,11 @@ def serializedATN(): 55, 56, 5, - 43, + 42, 0, 0, 56, - 14, + 12, 1, 0, 0, @@ -669,149 +682,149 @@ def serializedATN(): 57, 58, 5, - 115, + 43, 0, 0, 58, - 59, - 5, - 117, + 14, + 1, + 0, 0, 0, 59, 60, 5, - 109, + 115, 0, 0, 60, - 16, - 1, - 0, + 61, + 5, + 117, 0, 0, 61, 62, 5, - 115, + 109, 0, 0, 62, - 63, - 5, - 117, + 16, + 1, + 0, 0, 0, 63, 64, 5, - 109, + 115, 0, 0, 64, 65, 5, - 95, + 117, 0, 0, 65, 66, 5, - 99, + 109, 0, 0, 66, 67, 5, - 111, + 95, 0, 0, 67, 68, 5, - 110, + 99, 0, 0, 68, 69, 5, - 110, + 111, 0, 0, 69, 70, 5, - 101, + 110, 0, 0, 70, 71, 5, - 99, + 110, 0, 0, 71, 72, 5, - 116, + 101, 0, 0, 72, 73, 5, - 105, + 99, 0, 0, 73, 74, 5, - 111, + 116, 0, 0, 74, 75, 5, - 110, + 105, 0, 0, 75, 76, 5, - 115, + 111, 0, 0, 76, - 18, - 1, - 0, + 77, + 5, + 110, 0, 0, 77, 78, 5, - 46, + 115, 0, 0, 78, - 79, - 5, - 46, + 18, + 1, 0, 0, - 79, - 20, - 1, 0, + 79, + 80, + 5, + 46, 0, 0, 80, 81, 5, - 44, + 46, 0, 0, 81, - 22, + 20, 1, 0, 0, @@ -819,11 +832,11 @@ def serializedATN(): 82, 83, 5, - 91, + 44, 0, 0, 83, - 24, + 22, 1, 0, 0, @@ -831,23 +844,23 @@ def serializedATN(): 84, 85, 5, - 93, + 91, 0, 0, 85, - 26, + 24, 1, 0, 0, 0, 86, 87, - 7, - 0, + 5, + 93, 0, 0, 87, - 28, + 26, 1, 0, 0, @@ -855,287 +868,323 @@ def serializedATN(): 88, 89, 7, - 1, + 0, 0, 0, 89, - 30, + 28, 1, 0, 0, 0, 90, - 93, + 91, + 7, + 1, + 0, + 0, + 91, + 30, + 1, + 0, + 0, + 0, + 92, + 95, 3, 29, 14, 0, - 91, 93, + 95, 3, 27, 13, 0, + 94, 92, - 90, 1, 0, 0, 0, - 92, - 91, + 94, + 93, 1, 0, 0, 0, - 93, + 95, 32, 1, 0, 0, 0, - 94, 96, + 98, 3, 27, 13, 0, - 95, - 94, + 97, + 96, 1, 0, 0, 0, - 96, - 97, + 98, + 99, 1, 0, 0, 0, + 99, 97, - 95, 1, 0, 0, 0, - 97, - 98, + 99, + 100, 1, 0, 0, 0, - 98, - 105, + 100, + 107, 1, 0, 0, 0, - 99, 101, + 103, 5, 46, 0, 0, - 100, 102, + 104, 3, 27, 13, 0, - 101, - 100, + 103, + 102, 1, 0, 0, 0, - 102, - 103, + 104, + 105, 1, 0, 0, 0, + 105, 103, - 101, 1, 0, 0, 0, - 103, - 104, + 105, + 106, 1, 0, 0, 0, - 104, 106, + 108, 1, 0, 0, 0, - 105, - 99, + 107, + 101, 1, 0, 0, 0, - 105, - 106, + 107, + 108, 1, 0, 0, 0, - 106, + 108, 34, 1, 0, 0, 0, - 107, - 108, + 109, + 110, 5, - 116, + 109, 0, 0, - 108, + 110, + 111, + 5, + 97, + 0, + 0, + 111, + 112, + 5, + 120, + 0, + 0, + 112, 36, 1, 0, 0, 0, - 109, 113, + 114, + 5, + 116, + 0, + 0, + 114, + 38, + 1, + 0, + 0, + 0, + 115, + 119, 3, 29, 14, 0, - 110, - 112, + 116, + 118, 3, 31, 15, 0, - 111, - 110, + 117, + 116, 1, 0, 0, 0, - 112, - 115, + 118, + 121, 1, 0, 0, 0, - 113, - 111, + 119, + 117, 1, 0, 0, 0, - 113, - 114, + 119, + 120, 1, 0, 0, 0, - 114, - 38, + 120, + 40, 1, 0, 0, 0, - 115, - 113, + 121, + 119, 1, 0, 0, 0, - 116, 122, + 128, 5, 61, 0, 0, - 117, - 118, + 123, + 124, 5, 62, 0, 0, - 118, - 122, + 124, + 128, 5, 61, 0, 0, - 119, - 120, + 125, + 126, 5, 60, 0, 0, - 120, - 122, + 126, + 128, 5, 61, 0, 0, - 121, - 116, + 127, + 122, 1, 0, 0, 0, - 121, - 117, + 127, + 123, 1, 0, 0, 0, - 121, - 119, + 127, + 125, 1, 0, 0, 0, - 122, - 40, + 128, + 42, 1, 0, 0, 0, - 123, - 124, + 129, + 130, 7, 2, 0, 0, - 124, - 125, + 130, + 131, 1, 0, 0, 0, - 125, - 126, + 131, + 132, 6, - 20, + 21, 0, 0, - 126, - 42, + 132, + 44, 1, 0, 0, 0, 7, 0, - 92, - 97, - 103, + 94, + 99, 105, - 113, - 121, + 107, + 119, + 127, 1, 6, 0, @@ -1162,10 +1211,11 @@ class ExprLexer(Lexer): T__11 = 12 T__12 = 13 NUMBER = 14 - TIME = 15 - IDENTIFIER = 16 - COMPARISON = 17 - WS = 18 + MAX = 15 + TIME = 16 + IDENTIFIER = 17 + COMPARISON = 18 + WS = 19 channelNames = ["DEFAULT_TOKEN_CHANNEL", "HIDDEN"] @@ -1186,10 +1236,19 @@ class ExprLexer(Lexer): "','", "'['", "']'", + "'max'", "'t'", ] - symbolicNames = ["", "NUMBER", "TIME", "IDENTIFIER", "COMPARISON", "WS"] + symbolicNames = [ + "", + "NUMBER", + "MAX", + "TIME", + "IDENTIFIER", + "COMPARISON", + "WS", + ] ruleNames = [ "T__0", @@ -1209,6 +1268,7 @@ class ExprLexer(Lexer): "CHAR", "CHAR_OR_DIGIT", "NUMBER", + "MAX", "TIME", "IDENTIFIER", "COMPARISON", diff --git a/src/gems/expression/parsing/antlr/ExprLexer.tokens b/src/gems/expression/parsing/antlr/ExprLexer.tokens index c8638328..f367a876 100644 --- a/src/gems/expression/parsing/antlr/ExprLexer.tokens +++ b/src/gems/expression/parsing/antlr/ExprLexer.tokens @@ -12,10 +12,11 @@ T__10=11 T__11=12 T__12=13 NUMBER=14 -TIME=15 -IDENTIFIER=16 -COMPARISON=17 -WS=18 +MAX=15 +TIME=16 +IDENTIFIER=17 +COMPARISON=18 +WS=19 '.'=1 '-'=2 '('=3 @@ -29,4 +30,5 @@ WS=18 ','=11 '['=12 ']'=13 -'t'=15 +'max'=15 +'t'=16 diff --git a/src/gems/expression/parsing/antlr/ExprParser.py b/src/gems/expression/parsing/antlr/ExprParser.py index 859d23dc..3175ed5f 100644 --- a/src/gems/expression/parsing/antlr/ExprParser.py +++ b/src/gems/expression/parsing/antlr/ExprParser.py @@ -1,9 +1,8 @@ # Generated from Expr.g4 by ANTLR 4.13.2 # encoding: utf-8 -import sys -from io import StringIO - from antlr4 import * +from io import StringIO +import sys if sys.version_info[1] > 5: from typing import TextIO @@ -15,8 +14,8 @@ def serializedATN(): return [ 4, 1, - 18, - 140, + 19, + 152, 2, 0, 7, @@ -173,9 +172,35 @@ def serializedATN(): 2, 1, 2, + 1, + 2, + 1, + 2, + 1, + 2, + 1, + 2, + 1, + 2, + 5, + 2, + 84, + 8, + 2, + 10, + 2, + 12, + 2, + 87, + 9, + 2, + 1, + 2, + 1, + 2, 3, 2, - 79, + 91, 8, 2, 1, @@ -198,14 +223,14 @@ def serializedATN(): 2, 5, 2, - 90, + 102, 8, 2, 10, 2, 12, 2, - 93, + 105, 9, 2, 1, @@ -214,7 +239,7 @@ def serializedATN(): 3, 3, 3, - 97, + 109, 8, 3, 1, @@ -223,7 +248,7 @@ def serializedATN(): 4, 3, 4, - 101, + 113, 8, 4, 1, @@ -244,7 +269,7 @@ def serializedATN(): 5, 3, 5, - 111, + 123, 8, 5, 1, @@ -261,14 +286,14 @@ def serializedATN(): 5, 5, 5, - 119, + 131, 8, 5, 10, 5, 12, 5, - 122, + 134, 9, 5, 1, @@ -285,7 +310,7 @@ def serializedATN(): 6, 3, 6, - 130, + 142, 8, 6, 1, @@ -296,14 +321,14 @@ def serializedATN(): 6, 5, 6, - 135, + 147, 8, 6, 10, 6, 12, 6, - 138, + 150, 9, 6, 1, @@ -333,7 +358,7 @@ def serializedATN(): 2, 7, 7, - 153, + 167, 0, 14, 1, @@ -347,31 +372,31 @@ def serializedATN(): 0, 0, 4, - 78, + 90, 1, 0, 0, 0, 6, - 96, + 108, 1, 0, 0, 0, 8, - 98, + 110, 1, 0, 0, 0, 10, - 110, + 122, 1, 0, 0, 0, 12, - 129, + 141, 1, 0, 0, @@ -379,7 +404,7 @@ def serializedATN(): 14, 15, 5, - 16, + 17, 0, 0, 15, @@ -391,7 +416,7 @@ def serializedATN(): 16, 17, 5, - 16, + 17, 0, 0, 17, @@ -425,13 +450,13 @@ def serializedATN(): -1, 0, 22, - 79, + 91, 3, 6, 3, 0, 23, - 79, + 91, 3, 0, 0, @@ -443,11 +468,11 @@ def serializedATN(): 0, 0, 25, - 79, + 91, 3, 4, 2, - 13, + 14, 26, 27, 5, @@ -467,7 +492,7 @@ def serializedATN(): 0, 0, 29, - 79, + 91, 1, 0, 0, @@ -497,7 +522,7 @@ def serializedATN(): 0, 0, 34, - 79, + 91, 1, 0, 0, @@ -527,7 +552,7 @@ def serializedATN(): 0, 0, 39, - 79, + 91, 1, 0, 0, @@ -581,7 +606,7 @@ def serializedATN(): 0, 0, 48, - 79, + 91, 1, 0, 0, @@ -589,7 +614,7 @@ def serializedATN(): 49, 50, 5, - 16, + 17, 0, 0, 50, @@ -611,7 +636,7 @@ def serializedATN(): 0, 0, 53, - 79, + 91, 1, 0, 0, @@ -619,7 +644,7 @@ def serializedATN(): 54, 55, 5, - 16, + 17, 0, 0, 55, @@ -641,7 +666,7 @@ def serializedATN(): 0, 0, 58, - 79, + 91, 1, 0, 0, @@ -649,7 +674,7 @@ def serializedATN(): 59, 60, 5, - 16, + 17, 0, 0, 60, @@ -671,7 +696,7 @@ def serializedATN(): 0, 0, 63, - 79, + 91, 1, 0, 0, @@ -713,7 +738,7 @@ def serializedATN(): 0, 0, 70, - 79, + 91, 1, 0, 0, @@ -755,514 +780,599 @@ def serializedATN(): 0, 0, 77, - 79, + 91, 1, 0, 0, 0, 78, + 79, + 5, + 15, + 0, + 0, + 79, + 80, + 5, + 3, + 0, + 0, + 80, + 85, + 3, + 4, + 2, + 0, + 81, + 82, + 5, + 11, + 0, + 0, + 82, + 84, + 3, + 4, + 2, + 0, + 83, + 81, + 1, + 0, + 0, + 0, + 84, + 87, + 1, + 0, + 0, + 0, + 85, + 83, + 1, + 0, + 0, + 0, + 85, + 86, + 1, + 0, + 0, + 0, + 86, + 88, + 1, + 0, + 0, + 0, + 87, + 85, + 1, + 0, + 0, + 0, + 88, + 89, + 5, + 4, + 0, + 0, + 89, + 91, + 1, + 0, + 0, + 0, + 90, 21, 1, 0, 0, 0, - 78, + 90, 23, 1, 0, 0, 0, - 78, + 90, 24, 1, 0, 0, 0, - 78, + 90, 26, 1, 0, 0, 0, - 78, + 90, 30, 1, 0, 0, 0, - 78, + 90, 35, 1, 0, 0, 0, - 78, + 90, 40, 1, 0, 0, 0, - 78, + 90, 49, 1, 0, 0, 0, - 78, + 90, 54, 1, 0, 0, 0, - 78, + 90, 59, 1, 0, 0, 0, - 78, + 90, 64, 1, 0, 0, 0, - 78, + 90, 71, 1, 0, 0, 0, - 79, + 90, + 78, + 1, + 0, + 0, + 0, 91, + 103, 1, 0, 0, 0, - 80, - 81, + 92, + 93, 10, - 11, + 12, 0, 0, - 81, - 82, + 93, + 94, 7, 0, 0, 0, - 82, - 90, + 94, + 102, 3, 4, 2, - 12, - 83, - 84, - 10, + 13, + 95, + 96, 10, + 11, 0, 0, - 84, - 85, + 96, + 97, 7, 1, 0, 0, - 85, - 90, + 97, + 102, 3, 4, 2, - 11, - 86, - 87, + 12, + 98, + 99, + 10, 10, - 9, 0, 0, - 87, - 88, + 99, + 100, 5, - 17, + 18, 0, 0, - 88, - 90, + 100, + 102, 3, 4, 2, - 10, - 89, - 80, + 11, + 101, + 92, 1, 0, 0, 0, - 89, - 83, + 101, + 95, 1, 0, 0, 0, - 89, - 86, + 101, + 98, 1, 0, 0, 0, - 90, - 93, + 102, + 105, 1, 0, 0, 0, - 91, - 89, + 103, + 101, 1, 0, 0, 0, - 91, - 92, + 103, + 104, 1, 0, 0, 0, - 92, + 104, 5, 1, 0, 0, 0, - 93, - 91, + 105, + 103, 1, 0, 0, 0, - 94, - 97, + 106, + 109, 5, 14, 0, 0, - 95, - 97, + 107, + 109, 5, - 16, + 17, 0, 0, - 96, - 94, + 108, + 106, 1, 0, 0, 0, - 96, - 95, + 108, + 107, 1, 0, 0, 0, - 97, + 109, 7, 1, 0, 0, 0, - 98, - 100, + 110, + 112, 5, - 15, + 16, 0, 0, - 99, - 101, + 111, + 113, 3, 10, 5, 0, - 100, - 99, + 112, + 111, 1, 0, 0, 0, - 100, - 101, + 112, + 113, 1, 0, 0, 0, - 101, + 113, 9, 1, 0, 0, 0, - 102, - 103, + 114, + 115, 6, 5, -1, 0, - 103, - 104, + 115, + 116, 7, 1, 0, 0, - 104, - 111, + 116, + 123, 3, 6, 3, 0, - 105, - 106, + 117, + 118, 7, 1, 0, 0, - 106, - 107, + 118, + 119, 5, 3, 0, 0, - 107, - 108, + 119, + 120, 3, 4, 2, 0, - 108, - 109, + 120, + 121, 5, 4, 0, 0, - 109, - 111, + 121, + 123, 1, 0, 0, 0, - 110, - 102, + 122, + 114, 1, 0, 0, 0, - 110, - 105, + 122, + 117, 1, 0, 0, 0, - 111, - 120, + 123, + 132, 1, 0, 0, 0, - 112, - 113, + 124, + 125, 10, 4, 0, 0, - 113, - 114, + 125, + 126, 7, 0, 0, 0, - 114, - 119, + 126, + 131, 3, 12, 6, 0, - 115, - 116, + 127, + 128, 10, 3, 0, 0, - 116, - 117, + 128, + 129, 7, 1, 0, 0, - 117, - 119, + 129, + 131, 3, 12, 6, 0, - 118, - 112, + 130, + 124, 1, 0, 0, 0, - 118, - 115, + 130, + 127, 1, 0, 0, 0, - 119, - 122, + 131, + 134, 1, 0, 0, 0, - 120, - 118, + 132, + 130, 1, 0, 0, 0, - 120, - 121, + 132, + 133, 1, 0, 0, 0, - 121, + 133, 11, 1, 0, 0, 0, - 122, - 120, + 134, + 132, 1, 0, 0, 0, - 123, - 124, + 135, + 136, 6, 6, -1, 0, - 124, - 125, + 136, + 137, 5, 3, 0, 0, - 125, - 126, + 137, + 138, 3, 4, 2, 0, - 126, - 127, + 138, + 139, 5, 4, 0, 0, - 127, - 130, + 139, + 142, 1, 0, 0, 0, - 128, - 130, + 140, + 142, 3, 6, 3, 0, - 129, - 123, + 141, + 135, 1, 0, 0, 0, - 129, - 128, + 141, + 140, 1, 0, 0, 0, - 130, - 136, + 142, + 148, 1, 0, 0, 0, - 131, - 132, + 143, + 144, 10, 3, 0, 0, - 132, - 133, + 144, + 145, 7, 0, 0, 0, - 133, - 135, + 145, + 147, 3, 12, 6, 4, - 134, - 131, + 146, + 143, 1, 0, 0, 0, - 135, - 138, + 147, + 150, 1, 0, 0, 0, - 136, - 134, + 148, + 146, 1, 0, 0, 0, - 136, - 137, + 148, + 149, 1, 0, 0, 0, - 137, + 149, 13, 1, 0, 0, 0, - 138, - 136, + 150, + 148, 1, 0, 0, 0, - 10, - 78, - 89, - 91, - 96, - 100, - 110, - 118, - 120, - 129, - 136, + 11, + 85, + 90, + 101, + 103, + 108, + 112, + 122, + 130, + 132, + 141, + 148, ] @@ -1291,6 +1401,7 @@ class ExprParser(Parser): "'['", "']'", "", + "'max'", "'t'", ] @@ -1310,6 +1421,7 @@ class ExprParser(Parser): "", "", "NUMBER", + "MAX", "TIME", "IDENTIFIER", "COMPARISON", @@ -1349,10 +1461,11 @@ class ExprParser(Parser): T__11 = 12 T__12 = 13 NUMBER = 14 - TIME = 15 - IDENTIFIER = 16 - COMPARISON = 17 - WS = 18 + MAX = 15 + TIME = 16 + IDENTIFIER = 17 + COMPARISON = 18 + WS = 19 def __init__(self, input: TokenStream, output: TextIO = sys.stdout): super().__init__(input, output) @@ -1681,6 +1794,28 @@ def accept(self, visitor: ParseTreeVisitor): else: return visitor.visitChildren(self) + class MaxExprContext(ExprContext): + def __init__( + self, parser, ctx: ParserRuleContext + ): # actually a ExprParser.ExprContext + super().__init__(parser) + self.copyFrom(ctx) + + def MAX(self): + return self.getToken(ExprParser.MAX, 0) + + def expr(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(ExprParser.ExprContext) + else: + return self.getTypedRuleContext(ExprParser.ExprContext, i) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitMaxExpr"): + return visitor.visitMaxExpr(self) + else: + return visitor.visitChildren(self) + class TimeIndexContext(ExprContext): def __init__( self, parser, ctx: ParserRuleContext @@ -1748,9 +1883,9 @@ def expr(self, _p: int = 0): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 78 + self.state = 90 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input, 0, self._ctx) + la_ = self._interp.adaptivePredict(self._input, 1, self._ctx) if la_ == 1: localctx = ExprParser.UnsignedAtomContext(self, localctx) self._ctx = localctx @@ -1775,7 +1910,7 @@ def expr(self, _p: int = 0): self.state = 24 self.match(ExprParser.T__1) self.state = 25 - self.expr(13) + self.expr(14) pass elif la_ == 4: @@ -1918,18 +2053,44 @@ def expr(self, _p: int = 0): self.match(ExprParser.T__12) pass + elif la_ == 13: + localctx = ExprParser.MaxExprContext(self, localctx) + self._ctx = localctx + _prevctx = localctx + self.state = 78 + self.match(ExprParser.MAX) + self.state = 79 + self.match(ExprParser.T__2) + self.state = 80 + self.expr(0) + self.state = 85 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == 11: + self.state = 81 + self.match(ExprParser.T__10) + self.state = 82 + self.expr(0) + self.state = 87 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 88 + self.match(ExprParser.T__3) + pass + self._ctx.stop = self._input.LT(-1) - self.state = 91 + self.state = 103 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input, 2, self._ctx) + _alt = self._interp.adaptivePredict(self._input, 3, self._ctx) while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: if _alt == 1: if self._parseListeners is not None: self.triggerExitRuleEvent() _prevctx = localctx - self.state = 89 + self.state = 101 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input, 1, self._ctx) + la_ = self._interp.adaptivePredict(self._input, 2, self._ctx) if la_ == 1: localctx = ExprParser.MuldivContext( self, ExprParser.ExprContext(self, _parentctx, _parentState) @@ -1937,14 +2098,14 @@ def expr(self, _p: int = 0): self.pushNewRecursionContext( localctx, _startState, self.RULE_expr ) - self.state = 80 - if not self.precpred(self._ctx, 11): + self.state = 92 + if not self.precpred(self._ctx, 12): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException( - self, "self.precpred(self._ctx, 11)" + self, "self.precpred(self._ctx, 12)" ) - self.state = 81 + self.state = 93 localctx.op = self._input.LT(1) _la = self._input.LA(1) if not (_la == 5 or _la == 6): @@ -1952,8 +2113,8 @@ def expr(self, _p: int = 0): else: self._errHandler.reportMatch(self) self.consume() - self.state = 82 - self.expr(12) + self.state = 94 + self.expr(13) pass elif la_ == 2: @@ -1963,14 +2124,14 @@ def expr(self, _p: int = 0): self.pushNewRecursionContext( localctx, _startState, self.RULE_expr ) - self.state = 83 - if not self.precpred(self._ctx, 10): + self.state = 95 + if not self.precpred(self._ctx, 11): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException( - self, "self.precpred(self._ctx, 10)" + self, "self.precpred(self._ctx, 11)" ) - self.state = 84 + self.state = 96 localctx.op = self._input.LT(1) _la = self._input.LA(1) if not (_la == 2 or _la == 7): @@ -1978,8 +2139,8 @@ def expr(self, _p: int = 0): else: self._errHandler.reportMatch(self) self.consume() - self.state = 85 - self.expr(11) + self.state = 97 + self.expr(12) pass elif la_ == 3: @@ -1989,22 +2150,22 @@ def expr(self, _p: int = 0): self.pushNewRecursionContext( localctx, _startState, self.RULE_expr ) - self.state = 86 - if not self.precpred(self._ctx, 9): + self.state = 98 + if not self.precpred(self._ctx, 10): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException( - self, "self.precpred(self._ctx, 9)" + self, "self.precpred(self._ctx, 10)" ) - self.state = 87 + self.state = 99 self.match(ExprParser.COMPARISON) - self.state = 88 - self.expr(10) + self.state = 100 + self.expr(11) pass - self.state = 93 + self.state = 105 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input, 2, self._ctx) + _alt = self._interp.adaptivePredict(self._input, 3, self._ctx) except RecognitionException as re: localctx.exception = re @@ -2065,19 +2226,19 @@ def atom(self): localctx = ExprParser.AtomContext(self, self._ctx, self.state) self.enterRule(localctx, 6, self.RULE_atom) try: - self.state = 96 + self.state = 108 self._errHandler.sync(self) token = self._input.LA(1) if token in [14]: localctx = ExprParser.NumberContext(self, localctx) self.enterOuterAlt(localctx, 1) - self.state = 94 + self.state = 106 self.match(ExprParser.NUMBER) pass - elif token in [16]: + elif token in [17]: localctx = ExprParser.IdentifierContext(self, localctx) self.enterOuterAlt(localctx, 2) - self.state = 95 + self.state = 107 self.match(ExprParser.IDENTIFIER) pass else: @@ -2121,13 +2282,13 @@ def shift(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 98 + self.state = 110 self.match(ExprParser.TIME) - self.state = 100 + self.state = 112 self._errHandler.sync(self) _la = self._input.LA(1) if _la == 2 or _la == 7: - self.state = 99 + self.state = 111 self.shift_expr(0) except RecognitionException as re: @@ -2237,15 +2398,15 @@ def shift_expr(self, _p: int = 0): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 110 + self.state = 122 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input, 5, self._ctx) + la_ = self._interp.adaptivePredict(self._input, 6, self._ctx) if la_ == 1: localctx = ExprParser.SignedAtomContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 103 + self.state = 115 localctx.op = self._input.LT(1) _la = self._input.LA(1) if not (_la == 2 or _la == 7): @@ -2253,7 +2414,7 @@ def shift_expr(self, _p: int = 0): else: self._errHandler.reportMatch(self) self.consume() - self.state = 104 + self.state = 116 self.atom() pass @@ -2261,7 +2422,7 @@ def shift_expr(self, _p: int = 0): localctx = ExprParser.SignedExpressionContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 105 + self.state = 117 localctx.op = self._input.LT(1) _la = self._input.LA(1) if not (_la == 2 or _la == 7): @@ -2269,26 +2430,26 @@ def shift_expr(self, _p: int = 0): else: self._errHandler.reportMatch(self) self.consume() - self.state = 106 + self.state = 118 self.match(ExprParser.T__2) - self.state = 107 + self.state = 119 self.expr(0) - self.state = 108 + self.state = 120 self.match(ExprParser.T__3) pass self._ctx.stop = self._input.LT(-1) - self.state = 120 + self.state = 132 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input, 7, self._ctx) + _alt = self._interp.adaptivePredict(self._input, 8, self._ctx) while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: if _alt == 1: if self._parseListeners is not None: self.triggerExitRuleEvent() _prevctx = localctx - self.state = 118 + self.state = 130 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input, 6, self._ctx) + la_ = self._interp.adaptivePredict(self._input, 7, self._ctx) if la_ == 1: localctx = ExprParser.ShiftMuldivContext( self, @@ -2299,14 +2460,14 @@ def shift_expr(self, _p: int = 0): self.pushNewRecursionContext( localctx, _startState, self.RULE_shift_expr ) - self.state = 112 + self.state = 124 if not self.precpred(self._ctx, 4): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException( self, "self.precpred(self._ctx, 4)" ) - self.state = 113 + self.state = 125 localctx.op = self._input.LT(1) _la = self._input.LA(1) if not (_la == 5 or _la == 6): @@ -2314,7 +2475,7 @@ def shift_expr(self, _p: int = 0): else: self._errHandler.reportMatch(self) self.consume() - self.state = 114 + self.state = 126 self.right_expr(0) pass @@ -2328,14 +2489,14 @@ def shift_expr(self, _p: int = 0): self.pushNewRecursionContext( localctx, _startState, self.RULE_shift_expr ) - self.state = 115 + self.state = 127 if not self.precpred(self._ctx, 3): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException( self, "self.precpred(self._ctx, 3)" ) - self.state = 116 + self.state = 128 localctx.op = self._input.LT(1) _la = self._input.LA(1) if not (_la == 2 or _la == 7): @@ -2343,13 +2504,13 @@ def shift_expr(self, _p: int = 0): else: self._errHandler.reportMatch(self) self.consume() - self.state = 117 + self.state = 129 self.right_expr(0) pass - self.state = 122 + self.state = 134 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input, 7, self._ctx) + _alt = self._interp.adaptivePredict(self._input, 8, self._ctx) except RecognitionException as re: localctx.exception = re @@ -2436,7 +2597,7 @@ def right_expr(self, _p: int = 0): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 129 + self.state = 141 self._errHandler.sync(self) token = self._input.LA(1) if token in [3]: @@ -2444,27 +2605,27 @@ def right_expr(self, _p: int = 0): self._ctx = localctx _prevctx = localctx - self.state = 124 + self.state = 136 self.match(ExprParser.T__2) - self.state = 125 + self.state = 137 self.expr(0) - self.state = 126 + self.state = 138 self.match(ExprParser.T__3) pass - elif token in [14, 16]: + elif token in [14, 17]: localctx = ExprParser.RightAtomContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 128 + self.state = 140 self.atom() pass else: raise NoViableAltException(self) self._ctx.stop = self._input.LT(-1) - self.state = 136 + self.state = 148 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input, 9, self._ctx) + _alt = self._interp.adaptivePredict(self._input, 10, self._ctx) while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: if _alt == 1: if self._parseListeners is not None: @@ -2477,14 +2638,14 @@ def right_expr(self, _p: int = 0): self.pushNewRecursionContext( localctx, _startState, self.RULE_right_expr ) - self.state = 131 + self.state = 143 if not self.precpred(self._ctx, 3): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException( self, "self.precpred(self._ctx, 3)" ) - self.state = 132 + self.state = 144 localctx.op = self._input.LT(1) _la = self._input.LA(1) if not (_la == 5 or _la == 6): @@ -2492,11 +2653,11 @@ def right_expr(self, _p: int = 0): else: self._errHandler.reportMatch(self) self.consume() - self.state = 133 + self.state = 145 self.right_expr(4) - self.state = 138 + self.state = 150 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input, 9, self._ctx) + _alt = self._interp.adaptivePredict(self._input, 10, self._ctx) except RecognitionException as re: localctx.exception = re @@ -2520,13 +2681,13 @@ def sempred(self, localctx: RuleContext, ruleIndex: int, predIndex: int): def expr_sempred(self, localctx: ExprContext, predIndex: int): if predIndex == 0: - return self.precpred(self._ctx, 11) + return self.precpred(self._ctx, 12) if predIndex == 1: - return self.precpred(self._ctx, 10) + return self.precpred(self._ctx, 11) if predIndex == 2: - return self.precpred(self._ctx, 9) + return self.precpred(self._ctx, 10) def shift_expr_sempred(self, localctx: Shift_exprContext, predIndex: int): if predIndex == 3: diff --git a/src/gems/expression/parsing/antlr/ExprVisitor.py b/src/gems/expression/parsing/antlr/ExprVisitor.py index a98fbbaf..c79c9be7 100644 --- a/src/gems/expression/parsing/antlr/ExprVisitor.py +++ b/src/gems/expression/parsing/antlr/ExprVisitor.py @@ -66,6 +66,10 @@ def visitMuldiv(self, ctx: ExprParser.MuldivContext): def visitTimeSum(self, ctx: ExprParser.TimeSumContext): return self.visitChildren(ctx) + # Visit a parse tree produced by ExprParser#maxExpr. + def visitMaxExpr(self, ctx: ExprParser.MaxExprContext): + return self.visitChildren(ctx) + # Visit a parse tree produced by ExprParser#timeIndex. def visitTimeIndex(self, ctx: ExprParser.TimeIndexContext): return self.visitChildren(ctx) diff --git a/src/gems/expression/parsing/parse_expression.py b/src/gems/expression/parsing/parse_expression.py index a0bc80e6..491bf8e5 100644 --- a/src/gems/expression/parsing/parse_expression.py +++ b/src/gems/expression/parsing/parse_expression.py @@ -22,6 +22,7 @@ ComparisonNode, PortFieldAggregatorNode, PortFieldNode, + MaxNode, ) from gems.expression.parsing.antlr.ExprLexer import ExprLexer from gems.expression.parsing.antlr.ExprParser import ExprParser @@ -232,6 +233,10 @@ def visitRightMuldiv(self, ctx: ExprParser.RightMuldivContext) -> ExpressionNode def visitRightAtom(self, ctx: ExprParser.RightAtomContext) -> ExpressionNode: return ctx.atom().accept(self) # type: ignore + def visitMaxExpr(self, ctx: ExprParser.MaxExprContext) -> ExpressionNode: + operands: list[ExpressionNode] = [expr.accept(self) for expr in ctx.expr()] + return MaxNode(operands=operands) + _FUNCTIONS = { "expec": ExpressionNode.expec, diff --git a/src/gems/expression/print.py b/src/gems/expression/print.py index 50308d87..c5b67111 100644 --- a/src/gems/expression/print.py +++ b/src/gems/expression/print.py @@ -25,6 +25,7 @@ TimeEvalNode, TimeShiftNode, TimeSumNode, + MaxNode, ) from .expression import ( @@ -130,6 +131,10 @@ def port_field(self, node: PortFieldNode) -> str: def port_field_aggregator(self, node: PortFieldAggregatorNode) -> str: return f"({visit(node.operand, self)}.{node.aggregator})" + def max_node(self, node: MaxNode) -> str: + operand_strings = [visit(op, self) for op in node.operands] + return f"max({', '.join(operand_strings)})" + def print_expr(expression: ExpressionNode) -> str: return visit(expression, PrinterVisitor()) diff --git a/src/gems/expression/visitor.py b/src/gems/expression/visitor.py index 7180b6ab..5baafd6b 100644 --- a/src/gems/expression/visitor.py +++ b/src/gems/expression/visitor.py @@ -13,7 +13,7 @@ """ Defines abstract base class for visitors of expressions. """ -import typing + from abc import ABC, abstractmethod from typing import Generic, Protocol, TypeVar @@ -26,6 +26,7 @@ DivisionNode, ExpressionNode, LiteralNode, + MaxNode, MultiplicationNode, NegationNode, ParameterNode, @@ -128,6 +129,10 @@ def port_field(self, node: PortFieldNode) -> T: def port_field_aggregator(self, node: PortFieldAggregatorNode) -> T: ... + @abstractmethod + def max_node(self, node: MaxNode) -> T: + ... + def visit(root: ExpressionNode, visitor: ExpressionVisitor[T]) -> T: """ @@ -171,6 +176,8 @@ def visit(root: ExpressionNode, visitor: ExpressionVisitor[T]) -> T: return visitor.port_field(root) elif isinstance(root, PortFieldAggregatorNode): return visitor.port_field_aggregator(root) + elif isinstance(root, MaxNode): + return visitor.max_node(root) raise ValueError(f"Unknown expression node type {root.__class__}") @@ -228,3 +235,6 @@ def division(self, node: DivisionNode) -> T_op: left_value = visit(node.left, self) right_value = visit(node.right, self) return left_value / right_value + + def max_node(self, node: MaxNode) -> ExpressionNode: # type: ignore + return MaxNode(operands=[visit(op, self) for op in node.operands]) # type: ignore diff --git a/src/gems/model/port.py b/src/gems/model/port.py index cb07319b..c3724d66 100644 --- a/src/gems/model/port.py +++ b/src/gems/model/port.py @@ -30,6 +30,7 @@ BinaryOperatorNode, ComponentParameterNode, ComponentVariableNode, + MaxNode, PortFieldAggregatorNode, PortFieldNode, ProblemParameterNode, @@ -168,6 +169,9 @@ def port_field(self, node: PortFieldNode) -> None: def port_field_aggregator(self, node: PortFieldAggregatorNode) -> None: raise ValueError("Port definition cannot contain port field aggregation.") + def max_node(self, node: MaxNode) -> None: + raise ValueError("Port definition must not contain a max expression.") + def _validate_port_field_expression(definition: PortFieldDefinition) -> None: visit(definition.definition, _PortFieldExpressionChecker()) diff --git a/src/gems/simulation/linearize.py b/src/gems/simulation/linearize.py index 7e8dbf15..4c86aedd 100644 --- a/src/gems/simulation/linearize.py +++ b/src/gems/simulation/linearize.py @@ -29,6 +29,7 @@ CurrentScenarioIndex, ExpressionNode, LiteralNode, + MaxNode, NoScenarioIndex, NoTimeIndex, OneScenarioIndex, @@ -281,6 +282,14 @@ def port_field_aggregator( "Port fields aggregators must be replaced before linearization." ) + def max_node(self, node: MaxNode) -> LinearExpressionData: + operands = [visit(o, self) for o in node.operands] + terms: list = [] + if any(o.terms for o in operands): + raise ValueError("Cannot linearize max expressions with variable terms.") + max_const = max(o.constant for o in operands) + return LinearExpressionData(terms=terms, constant=max_const) + def linearize_expression( expression: ExpressionNode, diff --git a/tests/unittests/expressions/parsing/test_expression_parsing.py b/tests/unittests/expressions/parsing/test_expression_parsing.py index d6558d7f..ab9bfb15 100644 --- a/tests/unittests/expressions/parsing/test_expression_parsing.py +++ b/tests/unittests/expressions/parsing/test_expression_parsing.py @@ -13,7 +13,7 @@ import pytest -from gems.expression import ExpressionNode, literal, param, print_expr, var +from gems.expression import ExpressionNode, literal, max_expr, param, print_expr, var from gems.expression.equality import expressions_equal from gems.expression.expression import port_field from gems.expression.parsing.parse_expression import ( @@ -119,6 +119,12 @@ "expec(sum(cost * generation))", (param("cost") * var("generation")).time_sum().expec(), ), + ( + {}, + {}, + "max(1, 2)", + max_expr(literal(1), literal(2)), + ), ], ) def test_parsing_visitor( @@ -156,3 +162,24 @@ def test_parse_cancellation_should_throw(expression_str: str) -> None: match=r"An error occurred during parsing: ParseCancellationException", ): parse_expression(expression_str, identifiers) + + +@pytest.mark.parametrize( + "expression_str", + [ + "max(1 3)", + "max(x, y)", + ], +) +def test_parse_max_errors(expression_str: str) -> None: + # Console log error is displayed ! + identifiers = ModelIdentifiers( + variables={"x"}, + parameters=set(), + ) + + with pytest.raises( + AntaresParseException, + match=r"An error occurred during parsing:*", + ): + parse_expression(expression_str, identifiers) diff --git a/tests/unittests/expressions/visitor/test_copy.py b/tests/unittests/expressions/visitor/test_copy.py index 52a20b3f..5e258a34 100644 --- a/tests/unittests/expressions/visitor/test_copy.py +++ b/tests/unittests/expressions/visitor/test_copy.py @@ -10,6 +10,7 @@ # # This file is part of the Antares project. +import pytest from gems.expression import ( AdditionNode, @@ -23,23 +24,31 @@ from gems.expression.expression import ( AllTimeSumNode, ComponentParameterNode, + MaxNode, MultiplicationNode, TimeEvalNode, TimeShiftNode, ) -def test_copy_ast() -> None: - ast = AllTimeSumNode( - DivisionNode( - TimeEvalNode( - AdditionNode([LiteralNode(1), VariableNode("x")]), ParameterNode("p") - ), - TimeShiftNode( - MultiplicationNode(LiteralNode(1), VariableNode("x")), - ComponentParameterNode("comp1", "p"), +@pytest.mark.parametrize( + "ast", + [ + AllTimeSumNode( + DivisionNode( + TimeEvalNode( + AdditionNode([LiteralNode(1), VariableNode("x")]), + ParameterNode("p"), + ), + TimeShiftNode( + MultiplicationNode(LiteralNode(1), VariableNode("x")), + ComponentParameterNode("comp1", "p"), + ), ), ), - ) + MaxNode([LiteralNode(5), LiteralNode(10)]), + ], +) +def test_copy_ast(ast) -> None: copy = copy_expression(ast) assert expressions_equal(ast, copy) diff --git a/tests/unittests/expressions/visitor/test_degree.py b/tests/unittests/expressions/visitor/test_degree.py index 72996d64..8c16acb4 100644 --- a/tests/unittests/expressions/visitor/test_degree.py +++ b/tests/unittests/expressions/visitor/test_degree.py @@ -12,7 +12,14 @@ import pytest -from gems.expression import ExpressionDegreeVisitor, LiteralNode, param, var, visit +from gems.expression import ( + ExpressionDegreeVisitor, + LiteralNode, + max_expr, + param, + var, + visit, +) def test_degree() -> None: @@ -34,3 +41,8 @@ def test_degree_computation_should_take_into_account_simplifications() -> None: expr = LiteralNode(0) * x assert visit(expr, ExpressionDegreeVisitor()) == 0 + + +def test_max_degree() -> None: + expr = max_expr((5 * 3 + 3) / 1, 4) + assert visit(expr, ExpressionDegreeVisitor()) == 0 diff --git a/tests/unittests/expressions/visitor/test_equality.py b/tests/unittests/expressions/visitor/test_equality.py index bcdec8ff..0e5262bc 100644 --- a/tests/unittests/expressions/visitor/test_equality.py +++ b/tests/unittests/expressions/visitor/test_equality.py @@ -12,10 +12,31 @@ import pytest -from gems.expression import ExpressionNode, copy_expression, literal, param, var +from gems.expression import ( + ExpressionNode, + copy_expression, + literal, + max_expr, + param, + var, +) from gems.expression.equality import expressions_equal +@pytest.mark.parametrize( + "left, right, expected_equal", + [ + (max_expr(5, 87), max_expr(5, 87), True), + (max_expr(5, 87), max_expr(87, 5), False), + (max_expr(1, param("a")), max_expr(1, param("a")), True), + (max_expr(1, param("a")), max_expr(param("a"), 1), False), + (max_expr(1, param("a")), max_expr(1, param("b")), False), + ], +) +def test_max_expr_equality(left, right, expected_equal): + assert expressions_equal(left, right) == expected_equal + + @pytest.mark.parametrize( "expr", [ @@ -30,6 +51,7 @@ var("x").time_sum(), var("x") + 5 <= 2, var("x").expec(), + max_expr(1, param("a")), ], ) def test_equals(expr: ExpressionNode) -> None: diff --git a/tests/unittests/expressions/visitor/test_evaluation.py b/tests/unittests/expressions/visitor/test_evaluation.py index 62e25183..83093c31 100644 --- a/tests/unittests/expressions/visitor/test_evaluation.py +++ b/tests/unittests/expressions/visitor/test_evaluation.py @@ -22,6 +22,7 @@ EvaluationVisitor, ExpressionNode, LiteralNode, + MaxNode, ParameterNode, PrinterVisitor, ValueProvider, @@ -90,6 +91,12 @@ def test_ast() -> None: context = EvaluationContext(variables={"x": 3}, parameters={"p": 4}) assert visit(expr, EvaluationVisitor(context)) == 1 + expr = MaxNode(operands=[LiteralNode(1), ParameterNode("a")]) + assert visit(expr, PrinterVisitor()) == "max(1, a)" + + context = EvaluationContext(parameters={"a": 4}) + assert visit(expr, EvaluationVisitor(context)) == 4 + def test_operators() -> None: x = var("x") diff --git a/tests/unittests/expressions/visitor/test_indexing.py b/tests/unittests/expressions/visitor/test_indexing.py index 6a40f0d7..740cc3a0 100644 --- a/tests/unittests/expressions/visitor/test_indexing.py +++ b/tests/unittests/expressions/visitor/test_indexing.py @@ -10,8 +10,9 @@ # # This file is part of the Antares project. +import pytest -from gems.expression import param, var +from gems.expression import max_expr, param, var from gems.expression.indexing import IndexingStructureProvider, compute_indexation from gems.expression.indexing_structure import IndexingStructure @@ -42,6 +43,18 @@ def test_shift() -> None: assert compute_indexation(expr, provider) == IndexingStructure(True, True) +@pytest.mark.parametrize( + "expr, expected_indexing", + [ + (max_expr(param("a"), 3), IndexingStructure(True, True)), + (max_expr(1, 3), IndexingStructure(False, False)), + ], +) +def test_max(expr, expected_indexing) -> None: + provider = StructureProvider() + assert compute_indexation(expr, provider) == expected_indexing + + def test_time_eval() -> None: x = var("x") expr = x.eval(1) diff --git a/tests/unittests/expressions/visitor/test_linearization.py b/tests/unittests/expressions/visitor/test_linearization.py index 64a033a4..ef567878 100644 --- a/tests/unittests/expressions/visitor/test_linearization.py +++ b/tests/unittests/expressions/visitor/test_linearization.py @@ -21,6 +21,7 @@ TimeShift, comp_param, comp_var, + max_expr, problem_var, ) from gems.expression.indexing import IndexingStructureProvider @@ -128,6 +129,7 @@ def _expand_and_linearize( ((X + 2).time_sum(), X_at(t=0) + X_at(t=1) + constant(4)), ((X + 2).time_sum(-1, 0), X_at(t=-1) + X_at(t=0) + constant(4)), ((X + 2).time_sum(-1, 0), X_at(t=-1) + X_at(t=0) + constant(4)), + (max_expr(LiteralNode(2) + LiteralNode(1), LiteralNode(0)), constant(3)), ], ) def test_linearization_of_nested_time_operations( diff --git a/tests/unittests/expressions/visitor/test_max_expression_checker.py b/tests/unittests/expressions/visitor/test_max_expression_checker.py new file mode 100644 index 00000000..fe3664de --- /dev/null +++ b/tests/unittests/expressions/visitor/test_max_expression_checker.py @@ -0,0 +1,41 @@ +import pytest + +from gems.expression import ( + Comparator, + ComparisonNode, + ExpressionNode, + VariableNode, + max_expr, +) +from gems.expression.expression import ( + ComponentVariableNode, + PortFieldAggregatorNode, + PortFieldNode, + ProblemVariableNode, +) + + +@pytest.mark.parametrize( + "node, left_arg, right_arg", + [ + (VariableNode, ("x",), ("y",)), + ( + PortFieldAggregatorNode, + (ExpressionNode, "PortAggregator"), + (ExpressionNode, "PortAggregator"), + ), + (PortFieldNode, ("port", "name"), ("port", "name2")), + ( + ProblemVariableNode, + ("comp", "name", "time_index", "scenario_index"), + ("comp", "name", "time_index", "scenario_index"), + ), + (ComponentVariableNode, ("component_id", "name"), ("component_id2", "name2")), + (ComparisonNode, ("x", "y", Comparator.GREATER_THAN), 4), + ], +) +def test_max_expr_checker(node, left_arg, right_arg) -> None: + left = node(*left_arg) + right = right_arg if isinstance(right_arg, int) else node(*right_arg) + with pytest.raises(ValueError): + max_expr(left, right) diff --git a/tests/unittests/expressions/visitor/test_printer.py b/tests/unittests/expressions/visitor/test_printer.py index a41250af..7ca37e6a 100644 --- a/tests/unittests/expressions/visitor/test_printer.py +++ b/tests/unittests/expressions/visitor/test_printer.py @@ -10,12 +10,15 @@ # # This file is part of the Antares project. -from gems.expression import ExpressionNode, PrinterVisitor, param, var, visit +from gems.expression import ExpressionNode, PrinterVisitor, max_expr, param, var, visit def test_comparison() -> None: x = var("x") p = param("p") - expr: ExpressionNode = (5 * x + 3) >= p - 2 + expr: ExpressionNode = (5 * x + 3 + max_expr(5, 8)) >= p - 2 - assert visit(expr, PrinterVisitor()) == "((5.0 * x) + 3.0) >= (p - 2.0)" + assert ( + visit(expr, PrinterVisitor()) + == "((5.0 * x) + 3.0 + max(5.0, 8.0)) >= (p - 2.0)" + )