
com.sap.cds.impl.parser.ExprParser Maven / Gradle / Ivy
/*******************************************************************
* © 2020 SAP SE or an SAP affiliate company. All rights reserved. *
*******************************************************************/
package com.sap.cds.impl.parser;
import static com.sap.cds.impl.builder.model.ArithmeticExpr.dividedBy;
import static com.sap.cds.impl.builder.model.ArithmeticExpr.minus;
import static com.sap.cds.impl.builder.model.ArithmeticExpr.plus;
import static com.sap.cds.impl.builder.model.ArithmeticExpr.times;
import static com.sap.cds.impl.builder.model.ArithmeticNegation.negate;
import static com.sap.cds.impl.builder.model.StructuredTypeRefImpl.typeRef;
import static com.sap.cds.impl.parser.token.RefSegmentImpl.refSegment;
import static com.sap.cds.ql.cqn.CqnComparisonPredicate.Operator.EQ;
import static com.sap.cds.ql.cqn.CqnComparisonPredicate.Operator.GE;
import static com.sap.cds.ql.cqn.CqnComparisonPredicate.Operator.GT;
import static com.sap.cds.ql.cqn.CqnComparisonPredicate.Operator.IS;
import static com.sap.cds.ql.cqn.CqnComparisonPredicate.Operator.LE;
import static com.sap.cds.ql.cqn.CqnComparisonPredicate.Operator.LT;
import static com.sap.cds.ql.cqn.CqnComparisonPredicate.Operator.NE;
import static java.util.stream.Collectors.toList;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.stream.Stream;
import com.sap.cds.impl.builder.model.BetweenPredicate;
import com.sap.cds.impl.builder.model.ComparisonPredicate;
import com.sap.cds.impl.builder.model.CqnNull;
import com.sap.cds.impl.builder.model.ExistsSubQuery;
import com.sap.cds.impl.builder.model.InPredicate;
import com.sap.cds.impl.builder.model.MatchPredicate;
import com.sap.cds.impl.parser.builder.ExpressionBuilder;
import com.sap.cds.ql.Predicate;
import com.sap.cds.ql.cqn.CqnComparisonPredicate.Operator;
import com.sap.cds.ql.cqn.CqnPlain;
import com.sap.cds.ql.cqn.CqnPredicate;
import com.sap.cds.ql.cqn.CqnReference;
import com.sap.cds.ql.cqn.CqnReference.Segment;
import com.sap.cds.ql.cqn.CqnSelect;
import com.sap.cds.ql.cqn.CqnStructuredTypeRef;
import com.sap.cds.ql.cqn.CqnSubQuery;
import com.sap.cds.ql.cqn.CqnSyntaxException;
import com.sap.cds.ql.cqn.CqnToken;
import com.sap.cds.ql.cqn.CqnValue;
/**
*
* GRAMMAR:
*
* predicate = value pred_op value
*
* pred_op = '=' | '>' | '<' | '>=' | '<=' | '<>' | 'is' |
* 'in' | 'between' `
*
* value = numericExpression | val
*
* numericExpression = numericTerm [ ( '+' | '-' ) numericTerm ]*
*
* numericTerm = numericPrimary [ ( '*' | '/' ) numericPrimary ]*
*
* numericPrimary = '(' numericExpression ')' | numericValue
*
* numericValue = NUM_VAL | PLAIN_NUM | REF | PARAM | FUNC
*
* val = STRING_VAL | PLAIN_STRING
*/
public class ExprParser extends AbstractCqnExpressionParser {
public CqnValue parseValue(Stream tokens) {
return parseValue(tokens.collect(toList()));
}
public CqnValue parseValue(List tokenList) {
if (tokenList.isEmpty()) {
return null;
}
tokens = new LinkedList<>(tokenList);
aheadToken = tokens.getFirst();
pos = 0;
CqnValue value = value();
if (aheadToken != null) {
throw unexpected();
}
return value;
}
private CqnValue value() {
if (peek("(", "-", "+") || isNumeric()) {
return numericExpression();
}
return getValue();
}
private CqnValue numericExpression() {
CqnValue expr = numericTerm();
while (peek("+", "-")) {
if (is("+")) {
expr = plus(expr, numericTerm());
} else if (is("-")) {
expr = minus(expr, numericTerm());
}
}
return expr;
}
private CqnValue numericTerm() {
CqnValue term = numericFactor();
while (peek("*", "/")) {
if (is("*")) {
term = times(term, numericFactor());
} else if (is("/")) {
term = dividedBy(term, numericFactor());
}
}
return term;
}
private CqnValue numericFactor() {
if (is("+")) {
// nop
}
if (is("-")) {
CqnValue val = numericFactor();
return negate(val);
}
return numericPrimary();
}
private CqnValue numericPrimary() {
if (is("(")) {
CqnValue expr = numericExpression();
expect(")");
return expr;
}
return numericValue();
}
private CqnValue numericValue() {
if (!isNumeric()) {
throw new CqnSyntaxException("Unexpeceted non-numeric token");
}
return getValue();
}
@Override
protected CqnPredicate predicate() {
if (isPredicate()) {
return getPredicate();
}
if (is("exists")) {
return exists();
}
CqnValue value = value();
CqnPlain plain = getPlain();
switch (plain.plain().toLowerCase(Locale.US)) {
case "=":
return comparison(value, EQ);
case ">":
return comparison(value, GT);
case "<":
return comparison(value, LT);
case ">=":
return comparison(value, GE);
case "<=":
return comparison(value, LE);
case "<>":
return comparison(value, NE);
case "is":
return isNull(value, IS);
case "in":
return in(value);
case "between":
return between(value);
default:
List tokens = new ArrayList<>();
tokens.add(value);
tokens.add(plain);
while (hasNext() && !peek("and") && !peek("or") && !peek(")")) {
tokens.add(aheadToken);
nextToken();
}
return ExpressionBuilder.xpr(tokens.stream()).buildPredicate();
}
}
private CqnPredicate exists() {
if (isRef()) {
CqnReference ref = get(CqnReference.class);
int n = ref.segments().size();
List segments = new ArrayList<>(ref.segments().subList(0, n - 1));
segments.add(refSegment(ref.lastSegment()));
CqnStructuredTypeRef typeRef = typeRef(segments);
CqnPredicate predicate = ref.targetSegment().filter().orElse(null);
return MatchPredicate.any(typeRef, predicate);
}
return new ExistsSubQuery(getSelect());
}
private CqnSelect getSelect() {
if (aheadToken instanceof CqnSubQuery) {
CqnSelect select = ((CqnSubQuery) aheadToken).query();
nextToken();
return select;
}
return get(CqnSelect.class);
}
private CqnPredicate isNull(CqnValue value, Operator is) {
boolean negate = false;
if (is("not")) {
negate = true;
}
expect("null");
Predicate pred = ComparisonPredicate.pred(value, IS, CqnNull.getInstance());
if (negate) {
pred = pred.not();
}
return pred;
}
private CqnPredicate between(CqnValue value) {
CqnValue low = getValue();
expect("and"); // between needs special treatment because of this 'and'
CqnValue high = getValue();
return BetweenPredicate.between(value, low, high);
}
private CqnPredicate in(CqnValue value) {
expect("(");
List vs = new ArrayList<>();
vs.add(getValue());
while (is(",")) {
vs.add(getValue());
}
if (!is(")")) {
throw new CqnSyntaxException("Missing closing parenthesis");
}
return InPredicate.in(value, vs.stream());
}
private CqnPredicate comparison(CqnValue lhs, Operator op) {
return ComparisonPredicate.pred(lhs, op, value());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy