
com.sap.cds.impl.parser.AbstractCqnExpressionParser 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.parser.token.CqnPlainImpl.plain;
import static com.sap.cds.ql.CQL.and;
import static com.sap.cds.ql.CQL.not;
import static com.sap.cds.ql.CQL.or;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import com.sap.cds.impl.parser.token.CqnPlainImpl;
import com.sap.cds.ql.cqn.CqnListValue;
import com.sap.cds.ql.cqn.CqnLiteral;
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.CqnSyntaxException;
import com.sap.cds.ql.cqn.CqnToken;
import com.sap.cds.ql.cqn.CqnValue;
import com.sap.cds.ql.impl.Xpr;
/**
*
* GRAMMAR:
*
* search expression = boolean_term | boolean_term OR search_expression
*
* boolean_term = boolean_factor | boolean_factor AND boolean_term
*
* boolean_factor = [ NOT ] boolean_test
*
* boolean test = predicate | ( search_expression )
*/
public abstract class AbstractCqnExpressionParser {
protected LinkedList tokens;
protected CqnToken aheadToken;
protected int pos;
public CqnPredicate parsePredicate(List tokenList) {
return parsePredicate(tokenList.stream());
}
public CqnPredicate parsePredicate(Stream tokenStream) {
tokens = unfold(tokenStream);
if (tokens.isEmpty()) {
return null;
}
aheadToken = tokens.getFirst();
pos = 0;
CqnPredicate expression = searchCondition();
if (aheadToken != null) {
throw unexpected();
}
return expression;
}
private CqnPredicate searchCondition() {
CqnPredicate term = booleanTerm();
while (is("or")) {
term = or(term, booleanTerm());
}
return term;
}
private CqnPredicate booleanTerm() {
CqnPredicate factor = booleanFactor();
while (is("and")) {
factor = and(factor, booleanFactor());
}
return factor;
}
private CqnPredicate booleanFactor() {
if (is("not")) {
CqnPredicate factor = booleanFactor();
return not(factor);
}
return booleanTest();
}
private CqnPredicate booleanTest() {
if (peek("(")) {
return matchPredicate().orElseGet(() -> {
expect("(");
CqnPredicate expr = searchCondition();
expect(")");
return expr;
});
}
if (isXpr()) {
return matchPredicate().orElseGet(() -> new ExprParser().parsePredicate(getXpr().xpr()));
}
return predicate();
}
protected abstract CqnPredicate predicate();
protected void nextToken() {
tokens.pop();
if (tokens.isEmpty()) {
aheadToken = null;
} else {
aheadToken = tokens.getFirst();
pos++;
}
}
protected boolean hasNext() {
return aheadToken != null;
}
protected CqnPredicate getPredicate() {
return get(CqnPredicate.class);
}
protected CqnPlain getPlain() {
return get(CqnPlain.class);
}
protected CqnLiteral> getLiteral() {
return get(CqnLiteral.class);
}
protected Xpr getXpr() {
return get(Xpr.class);
}
protected CqnValue getValue() {
return get(CqnValue.class);
}
protected CqnListValue getList() {
return get(CqnListValue.class);
}
@SuppressWarnings("unchecked")
protected T get(Class clazz) {
if (null == aheadToken) {
throw new CqnSyntaxException("Unexpected end of token stream.");
}
if (clazz.isAssignableFrom(aheadToken.getClass())) {
T value = (T) aheadToken;
nextToken();
return value;
}
throw unexpected();
}
// TODO name is misleading
protected boolean is(String value) {
if (peek(value)) {
nextToken();
return true;
}
return false;
}
protected boolean peek(String... values) {
if (!isPlain()) {
return false;
}
String plain = ((CqnPlain) aheadToken).plain();
return Arrays.stream(values).anyMatch(plain::equalsIgnoreCase);
}
protected boolean isPredicate() {
return aheadToken instanceof CqnPredicate;
}
protected boolean isPlain() {
return aheadToken instanceof CqnPlain;
}
protected boolean isLiteral() {
return aheadToken instanceof CqnLiteral;
}
protected boolean isRef() {
return aheadToken instanceof CqnReference;
}
protected boolean isXpr() {
return aheadToken instanceof Xpr;
}
protected boolean isList() {
return aheadToken instanceof CqnListValue;
}
protected CqnSyntaxException unexpected() {
String msg = MessageFormat.format("Unexpected token at position {0}: {1}", pos, aheadToken.toJson());
return new CqnSyntaxException(msg);
}
protected void expect(String expected) {
if (!is(expected)) {
throw expecting(plain(expected));
}
}
private CqnSyntaxException expecting(CqnPlainImpl plain) {
String msg;
if (aheadToken != null) {
msg = MessageFormat.format("Unexpeceted token {0} at position {1}. Expecting {2}.", aheadToken.toJson(),
pos, plain.toJson());
} else {
msg = MessageFormat.format("Expecting token {0} at position {1}.", plain.toJson(), pos);
}
return new CqnSyntaxException(msg);
}
private static LinkedList unfold(Stream tokens) {
LinkedList list = new LinkedList<>();
tokens.forEach(list::add);
return list;
}
private Optional matchPredicate() {
// stash
LinkedList oldTokens = new LinkedList<>(tokens);
CqnToken oldAhead = aheadToken;
int oldPos = pos;
try {
return Optional.of(predicate());
} catch (Exception ex) {
// unstash
tokens = oldTokens;
aheadToken = oldAhead;
pos = oldPos;
// can't be parsed
return Optional.empty();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy