in metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/StellarCompiler.java [156:282]
public Object apply(ExpressionState state) {
Deque<Token<?>> instanceDeque = new ArrayDeque<>();
{
int skipElseCount = 0;
boolean skipMatchClauses = false;
Token<?> token = null;
for (Iterator<Token<?>> it = getTokenDeque().descendingIterator(); it.hasNext(); ) {
token = it.next();
//if we've skipped an else previously, then we need to skip the deferred tokens associated with the else.
if (skipElseCount > 0 && token.getUnderlyingType() == ElseExpr.class) {
while (it.hasNext()) {
token = it.next();
if (token.getUnderlyingType() == EndConditional.class) {
break;
}
}
// We've completed a single else.
skipElseCount--;
}
if (skipMatchClauses && (token.getUnderlyingType() == MatchClauseEnd.class
|| token.getUnderlyingType() == MatchClauseCheckExpr.class)) {
while (it.hasNext()) {
token = it.next();
if (token.getUnderlyingType() == MatchClausesEnd.class) {
break;
}
}
skipMatchClauses = false;
}
/*
curr is the current value on the stack. This is the non-deferred actual evaluation for this expression
and with the current context.
*/
Token<?> curr = instanceDeque.peek();
boolean isFalsey = curr != null &&
(isBoolean(token, curr.getValue()) || isEmptyList(token, curr.getValue()));
if(isFalsey){
//If we're in a situation where the token is a boolean token and the current value is one of the implicitly falsey scenarios
//* null or missing variable
//* empty list
// then we want to treat it as explicitly false by replacing the current token.
curr = new Token<>(false, Boolean.class, curr.getMultiArgContext());
instanceDeque.removeFirst();
instanceDeque.addFirst(curr);
}
if (curr != null && curr.getValue() != null && curr.getValue() instanceof Boolean
&& ShortCircuitOp.class.isAssignableFrom(token.getUnderlyingType())) {
//if we have a boolean as the current value and the next non-contextual token is a short circuit op
//then we need to short circuit possibly
if (token.getUnderlyingType() == BooleanArg.class) {
if (token.getMultiArgContext() != null
&& token.getMultiArgContext().getVariety() == FrameContext.BOOLEAN_OR
&& (Boolean) (curr.getValue())) {
//short circuit the or
FrameContext.Context context = curr.getMultiArgContext();
shortCircuit(it, context);
} else if (token.getMultiArgContext() != null
&& token.getMultiArgContext().getVariety() == FrameContext.BOOLEAN_AND
&& !(Boolean) (curr.getValue())) {
//short circuit the and
FrameContext.Context context = curr.getMultiArgContext();
shortCircuit(it, context);
}
} else if (token.getUnderlyingType() == IfExpr.class) {
//short circuit the if/then/else
instanceDeque.pop();
if((Boolean)curr.getValue()) {
//choose then. Need to make sure we're keeping track of nesting.
skipElseCount++;
} else {
//choose else
// Need to count in case we see another if-else, to avoid breaking on wrong else.
int innerIfCount = 0;
while (it.hasNext()) {
Token<?> t = it.next();
if (t.getUnderlyingType() == IfExpr.class) {
innerIfCount++;
} else if (t.getUnderlyingType() == ElseExpr.class) {
if (innerIfCount == 0) {
break;
} else {
innerIfCount--;
}
}
}
}
} else if (token.getUnderlyingType() == MatchClauseCheckExpr.class) {
instanceDeque.pop();
if ((Boolean) curr.getValue()) {
//skip everything else after lambda
skipMatchClauses = true;
} else {
while (it.hasNext()) {
Token<?> t = it.next();
if (t.getUnderlyingType() == MatchClauseEnd.class) {
break;
}
}
}
}
}
if (token.getUnderlyingType() == DeferredFunction.class) {
DeferredFunction func = (DeferredFunction) token.getValue();
func.apply(instanceDeque, state);
}
else if(token.getUnderlyingType() != ShortCircuitFrame.class
&& !ShortCircuitOp.class.isAssignableFrom(token.getUnderlyingType())
) {
instanceDeque.push(token);
}
}
}
if (instanceDeque.isEmpty()) {
throw new ParseException("Invalid predicate: Empty stack.");
}
Token<?> token = instanceDeque.pop();
if (instanceDeque.isEmpty()) {
return token.getValue();
}
if (instanceDeque.isEmpty()) {
throw new ParseException("Invalid parse, stack not empty: " + Joiner.on(',').join(instanceDeque));
} else {
throw new ParseException("Invalid parse, found " + token);
}
}