/*
***************************************************************************************
* Copyright (C) 2006 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
***************************************************************************************
*/
package com.espertech.esper.epl.parse;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.epl.spec.PatternStreamSpecRaw;
import com.espertech.esper.pattern.PatternExpressionPrecedenceEnum;
import com.espertech.esper.supportunit.epl.parse.SupportParserHelper;
import junit.framework.TestCase;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.Tree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.StringWriter;
/**
* Test operator precedence and on-expression equivalence.
* Precendences are similar to Java see <a>http://java.sun.com/docs/books/tutorial/java/nutsandbolts/expressions.html</a>)
* <p>
* Precedence ordering (highest on top):
* postfix operators - within
* unary operators - not, every
* AND - and
* OR - or
* FOLLOWED BY - ->
*/
public class TestParserOpPrecedence extends TestCase {
public void testEquivalency() throws Exception {
assertEquivalent("every a",
"(every a)");
assertEquivalent("every a() or b()",
"((every a()) or b())");
assertEquivalent("every a -> b or c",
"(every a) -> (b or c)");
assertEquivalent("every a() -> b() and c()",
"(every a()) -> (b() and c())");
assertEquivalent("a() and b() or c()",
"(a() and b()) or c()");
assertEquivalent("a() or b() and c() or d()",
"a() or (b() and c()) or d()");
assertEquivalent("a() or b() and every e() -> f() -> c() or d()",
"(a() or (b() and (every (e())))) -> f() -> (c() or d())");
assertEquivalent("a() -> b() or e() -> f()",
"a() -> (b() or e()) -> f()");
String original = "every a() -> every b() and c() or d() and not e() -> f()";
assertEquivalent(original, "every a() -> (every b()) and c() or d() and (not (e())) -> f()");
assertEquivalent(original, "(every a()) -> ((every b()) and c()) or (d() and (not (e()))) -> f()");
assertEquivalent("not a()",
"(not a())");
assertEquivalent("every a() where timer:within(5)",
"every (a() where timer:within(5))");
original = "every a() where timer:within(5) and not b() where timer:within(3) -> d() where timer:within(4)";
assertEquivalent(original,
"every (a() where timer:within(5)) and not (b() where timer:within(3)) -> (d() where timer:within(4))");
assertEquivalent(original,
"(every (a() where timer:within(5))) and (not (b() where timer:within(3))) -> (d() where timer:within(4))");
assertEquivalent(original,
"((every (a() where timer:within(5))) and (not (b() where timer:within(3)))) -> (d() where timer:within(4))");
assertEquivalent("((a() where timer:within(10)) or (b() where timer:within(5))) where timer:within(20)",
"(a() where timer:within(10) or b() where timer:within(5)) where timer:within(20)");
assertEquivalent("timer:interval(20)", "(timer:interval(20))");
assertEquivalent("every timer:interval(20)", "every (timer:interval(20))");
assertEquivalent("timer:interval(20) -> timer:interval(20) or timer:interval(22)", "((timer:interval(20)) -> (timer:interval(20) or timer:interval(22)))");
assertEquivalent("every a() -> every timer:interval(20) -> every c()", "(every a()) -> (every (timer:interval(20))) -> (every c())");
original = "timer:at(5,0,[1,2],1:10,* /9,[1,2,5:8]) -> b()";
assertEquivalent(original, original);
}
public void testNotEquivalent() throws Exception {
assertNotEquivalent("a()", "every a()");
assertNotEquivalent("a(n=6)", "a(n=7)");
assertNotEquivalent("a(x=\"a\")", "a(x=\"b\")");
assertNotEquivalent("a()", "b()");
assertNotEquivalent("a() where timer:within(20)", "a() where timer:within(30)");
assertNotEquivalent("a() or b() where timer:within(20)", "(a() or b()) where timer:within(20)");
assertNotEquivalent("every a() or b()", "every (a() or b())");
assertNotEquivalent("every a() and b()", "every (a() and b())");
assertNotEquivalent("a() -> not b()", "not(a() -> b())");
assertNotEquivalent("a() -> b() or c()", "(a() -> b()) or c()");
assertNotEquivalent("a() and b() or c()", "a() and (b() or c())");
assertNotEquivalent("timer:interval(20)", "timer:interval(30)");
assertNotEquivalent("timer:at(20,*,*,*,*)", "timer:at(21,*,*,*,*)");
assertNotEquivalent("timer:at([1:10],*,*,*,*)", "timer:at([1:11],*,*,*,*)");
assertNotEquivalent("timer:at(*,*,3:2,*,*)", "timer:at(*,*,2:3,*,*)");
assertNotEquivalent("EventA(value in [2:5])", "EventA(value in [3:5])");
assertNotEquivalent("EventA(value in [2:5])", "EventA(value in [2:6])");
assertNotEquivalent("EventA(value in [2:5])", "EventA(value in (2:6])");
assertNotEquivalent("EventA(value in [2:5])", "EventA(value in [2:6))");
assertNotEquivalent("EventA(value in [2:5])", "EventA(value in (2:6))");
}
private void assertEquivalent(String expressionOne, String expressionTwo) throws Exception {
EPLTreeWalkerListener l1 = SupportParserHelper.parseAndWalkPattern(expressionOne);
EPLTreeWalkerListener l2 = SupportParserHelper.parseAndWalkPattern(expressionTwo);
String t1 = toPatternText(l1);
String t2 = toPatternText(l2);
assertEquals(t1, t2);
}
private String toPatternText(EPLTreeWalkerListener walker) {
PatternStreamSpecRaw raw = (PatternStreamSpecRaw) walker.getStatementSpec().getStreamSpecs().get(0);
StringWriter writer = new StringWriter();
raw.getEvalFactoryNode().toEPL(writer, PatternExpressionPrecedenceEnum.MINIMUM);
return writer.toString();
}
private void assertNotEquivalent(String expressionOne, String expressionTwo) throws Exception {
log.debug(".assertEquivalent parsing: " + expressionOne);
Pair<Tree, CommonTokenStream> astOne = parse(expressionOne);
log.debug(".assertEquivalent parsing: " + expressionTwo);
Pair<Tree, CommonTokenStream> astTwo = parse(expressionTwo);
assertFalse(astOne.getFirst().toStringTree().equals(astTwo.getFirst().toStringTree()));
}
private Pair<Tree, CommonTokenStream> parse(String expression) throws Exception {
return SupportParserHelper.parsePattern(expression);
}
private static final Logger log = LoggerFactory.getLogger(TestParserOpPrecedence.class);
}