dev.cel.parser.Operator Maven / Gradle / Ivy
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dev.cel.parser;
import com.google.common.collect.ImmutableMap;
import dev.cel.common.ast.CelExpr;
import java.util.Objects;
import java.util.Optional;
/**
* Package-private enumeration of Common Expression Language operators.
*
* Equivalent to https://pkg.go.dev/github.com/google/cel-go/common/operators.
*/
public enum Operator {
CONDITIONAL("_?_:_"),
LOGICAL_AND("_&&_", "&&"),
LOGICAL_OR("_||_", "||"),
LOGICAL_NOT("!_", "!"),
EQUALS("_==_", "=="),
NOT_EQUALS("_!=_", "!="),
LESS("_<_", "<"),
LESS_EQUALS("_<=_", "<="),
GREATER("_>_", ">"),
GREATER_EQUALS("_>=_", ">="),
ADD("_+_", "+"),
SUBTRACT("_-_", "-"),
MULTIPLY("_*_", "*"),
DIVIDE("_/_", "/"),
MODULO("_%_", "%"),
NEGATE("-_", "-"),
INDEX("_[_]"),
HAS("has"),
ALL("all"),
EXISTS("exists"),
EXISTS_ONE("exists_one"),
MAP("map"),
FILTER("filter"),
NOT_STRICTLY_FALSE("@not_strictly_false"),
IN("@in", "in"),
OPTIONAL_INDEX("_[?_]"),
OPTIONAL_SELECT("_?._"),
@Deprecated // Prefer NOT_STRICTLY_FALSE.
OLD_NOT_STRICTLY_FALSE("__not_strictly_false__"),
@Deprecated // Prefer IN.
OLD_IN("_in_");
private final String functionName;
private final String displayName;
Operator(String functionName) {
this(functionName, "");
}
Operator(String functionName, String displayName) {
this.functionName = functionName;
this.displayName = displayName;
}
/** Returns the mangled operator name, as used within the AST. */
public String getFunction() {
return functionName;
}
/** Returns the unmangled operator name, as used within the source text of an expression. */
String getSymbol() {
return displayName;
}
private static final ImmutableMap OPERATORS =
ImmutableMap.builder()
.put(ADD.getSymbol(), ADD)
.put(DIVIDE.getSymbol(), DIVIDE)
.put(EQUALS.getSymbol(), EQUALS)
.put(GREATER.getSymbol(), GREATER)
.put(GREATER_EQUALS.getSymbol(), GREATER_EQUALS)
.put(IN.getSymbol(), IN)
.put(LESS.getSymbol(), LESS)
.put(LESS_EQUALS.getSymbol(), LESS_EQUALS)
.put(MODULO.getSymbol(), MODULO)
.put(MULTIPLY.getSymbol(), MULTIPLY)
.put(NOT_EQUALS.getSymbol(), NOT_EQUALS)
.put(SUBTRACT.getSymbol(), SUBTRACT)
.buildOrThrow();
/** Lookup an operator by its unmangled name, as used with the source text of an expression. */
static Optional find(String text) {
return Optional.ofNullable(OPERATORS.get(text));
}
private static final ImmutableMap REVERSE_OPERATORS =
ImmutableMap.builder()
.put(ADD.getFunction(), ADD)
.put(DIVIDE.getFunction(), DIVIDE)
.put(EQUALS.getFunction(), EQUALS)
.put(GREATER.getFunction(), GREATER)
.put(GREATER_EQUALS.getFunction(), GREATER_EQUALS)
.put(IN.getFunction(), IN)
.put(LESS.getFunction(), LESS)
.put(LESS_EQUALS.getFunction(), LESS_EQUALS)
.put(LOGICAL_AND.getFunction(), LOGICAL_AND)
.put(LOGICAL_NOT.getFunction(), LOGICAL_NOT)
.put(LOGICAL_OR.getFunction(), LOGICAL_OR)
.put(MODULO.getFunction(), MODULO)
.put(MULTIPLY.getFunction(), MULTIPLY)
.put(NEGATE.getFunction(), NEGATE)
.put(NOT_EQUALS.getFunction(), NOT_EQUALS)
.put(SUBTRACT.getFunction(), SUBTRACT)
.put(OLD_IN.getFunction(), OLD_IN)
.buildOrThrow();
// precedence of the operator, where the higher value means higher.
private static final ImmutableMap PRECEDENCES =
ImmutableMap.builder()
.put(CONDITIONAL.getFunction(), 8)
.put(LOGICAL_OR.getFunction(), 7)
.put(LOGICAL_AND.getFunction(), 6)
.put(EQUALS.getFunction(), 5)
.put(GREATER.getFunction(), 5)
.put(GREATER_EQUALS.getFunction(), 5)
.put(IN.getFunction(), 5)
.put(LESS.getFunction(), 5)
.put(LESS_EQUALS.getFunction(), 5)
.put(NOT_EQUALS.getFunction(), 5)
.put(ADD.getFunction(), 4)
.put(SUBTRACT.getFunction(), 4)
.put(DIVIDE.getFunction(), 3)
.put(MODULO.getFunction(), 3)
.put(MULTIPLY.getFunction(), 3)
.put(LOGICAL_NOT.getFunction(), 2)
.put(NEGATE.getFunction(), 2)
.put(INDEX.getFunction(), 1)
.buildOrThrow();
private static final ImmutableMap UNARY_OPERATORS =
ImmutableMap.builder()
.put(NEGATE.getFunction(), "-")
.put(LOGICAL_NOT.getFunction(), "!")
.buildOrThrow();
private static final ImmutableMap BINARY_OPERATORS =
ImmutableMap.builder()
.put(LOGICAL_OR.getFunction(), "||")
.put(LOGICAL_AND.getFunction(), "&&")
.put(LESS_EQUALS.getFunction(), "<=")
.put(LESS.getFunction(), "<")
.put(GREATER_EQUALS.getFunction(), ">=")
.put(GREATER.getFunction(), ">")
.put(EQUALS.getFunction(), "==")
.put(NOT_EQUALS.getFunction(), "!=")
.put(IN.getFunction(), "in")
.put(ADD.getFunction(), "+")
.put(SUBTRACT.getFunction(), "-")
.put(MULTIPLY.getFunction(), "*")
.put(DIVIDE.getFunction(), "/")
.put(MODULO.getFunction(), "%")
.buildOrThrow();
/** Lookup an operator by its mangled name, as used within the AST. */
static Optional findReverse(String op) {
return Optional.ofNullable(REVERSE_OPERATORS.get(op));
}
/** Lookup a binary operator by its mangled name, as used within the AST. */
static Optional findReverseBinaryOperator(String op) {
if (Objects.equals(op, LOGICAL_NOT.getFunction()) || Objects.equals(op, NEGATE.getFunction())) {
return Optional.empty();
}
return Optional.ofNullable(REVERSE_OPERATORS.get(op));
}
static int lookupPrecedence(String op) {
return PRECEDENCES.getOrDefault(op, 0);
}
static Optional lookupUnaryOperator(String op) {
return Optional.ofNullable(UNARY_OPERATORS.get(op));
}
static Optional lookupBinaryOperator(String op) {
return Optional.ofNullable(BINARY_OPERATORS.get(op));
}
static boolean isOperatorLowerPrecedence(String op, CelExpr expr) {
if (!expr.exprKind().getKind().equals(CelExpr.ExprKind.Kind.CALL)) {
return false;
}
return lookupPrecedence(op) < lookupPrecedence(expr.call().function());
}
static boolean isOperatorLeftRecursive(String op) {
return !op.equals(LOGICAL_AND.getFunction()) && !op.equals(LOGICAL_OR.getFunction());
}
}