package openmods.calc; import openmods.calc.parsing.AstCompiler; import openmods.calc.parsing.DefaultExprNodeFactory; import openmods.calc.parsing.IAstParser; import openmods.calc.parsing.ICompilerState; import openmods.calc.parsing.InfixParser; import openmods.calc.parsing.Token; import openmods.calc.parsing.UnfinishedExpressionException; import openmods.calc.parsing.UnmatchedBracketsException; import org.junit.Test; public class InfixCompilerTest extends CalcTestUtils { public final OperatorDictionary<String> operators = new OperatorDictionary<String>(); { operators.registerBinaryOperator(PLUS); operators.registerUnaryOperator(UNARY_PLUS); operators.registerBinaryOperator(MINUS); operators.registerUnaryOperator(UNARY_MINUS); operators.registerUnaryOperator(UNARY_NEG); operators.registerBinaryOperator(MULTIPLY); // token for default added only for testing purposes operators.registerBinaryOperator(DEFAULT).setDefault(); } private final ICompilerState<String> testState = new TestCompilerState() { @Override public IAstParser<String> getParser() { return new InfixParser<String>(operators, new DefaultExprNodeFactory<String>(VALUE_PARSER)); } }; private CompilerResultTester given(Token... inputs) { return new CompilerResultTester(new AstCompiler<String>(testState), inputs); } @Test public void testSimpleExpr() { // 1 + 2 given(dec("1"), OP_PLUS, dec("2")) .expect(c("1"), c("2"), PLUS); given(dec("1"), OP_MINUS, dec("2")) .expect(c("1"), c("2"), MINUS); } @Test public void testMultipleSamePrecendenceOps() { // 1 + 2 - 3 given(dec("1"), OP_PLUS, dec("2"), OP_MINUS, dec("3")) .expect(c("1"), c("2"), PLUS, c("3"), MINUS); } @Test public void testMultipleDifferentPrecendenceOps() { // 1 + 2 * 3 given(dec("1"), OP_PLUS, dec("2"), OP_MULTIPLY, dec("3")) .expect(c("1"), c("2"), c("3"), MULTIPLY, PLUS); // 1 * 2 + 3 given(dec("1"), OP_MULTIPLY, dec("2"), OP_PLUS, dec("3")) .expect(c("1"), c("2"), MULTIPLY, c("3"), PLUS); } @Test public void testBrackets() { // (1 + 2) * 3 given(LEFT_BRACKET, dec("1"), OP_PLUS, dec("2"), RIGHT_BRACKET, OP_MULTIPLY, dec("3")) .expect(c("1"), c("2"), PLUS, c("3"), MULTIPLY); // 1 * (2 + 3) given(dec("1"), OP_MULTIPLY, LEFT_BRACKET, dec("2"), OP_PLUS, dec("3"), RIGHT_BRACKET) .expect(c("1"), c("2"), c("3"), PLUS, MULTIPLY); // [1] given(leftBracket("["), dec("1"), rightBracket("]")) .expect(c("1")); // {1} given(leftBracket("{"), dec("1"), rightBracket("}")) .expect(c("1")); } @Test public void testNestedBrackets() { // (1 + (2 - (3))) given(LEFT_BRACKET, dec("1"), OP_PLUS, LEFT_BRACKET, dec("2"), OP_MINUS, LEFT_BRACKET, dec("3"), RIGHT_BRACKET, RIGHT_BRACKET, RIGHT_BRACKET) .expect(c("1"), c("2"), c("3"), MINUS, PLUS); // ((1)) given(LEFT_BRACKET, LEFT_BRACKET, dec("1"), RIGHT_BRACKET, RIGHT_BRACKET) .expect(c("1")); // (((1))) given(LEFT_BRACKET, LEFT_BRACKET, LEFT_BRACKET, dec("1"), RIGHT_BRACKET, RIGHT_BRACKET, RIGHT_BRACKET) .expect(c("1")); // (((1) + 2) - 3) given(LEFT_BRACKET, LEFT_BRACKET, LEFT_BRACKET, dec("1"), RIGHT_BRACKET, OP_PLUS, dec("2"), RIGHT_BRACKET, OP_MINUS, dec("3"), RIGHT_BRACKET) .expect(c("1"), c("2"), PLUS, c("3"), MINUS); // [{(1) + 2} - 3] given(leftBracket("["), leftBracket("{"), LEFT_BRACKET, dec("1"), RIGHT_BRACKET, OP_PLUS, dec("2"), rightBracket("}"), OP_MINUS, dec("3"), rightBracket("]")) .expect(c("1"), c("2"), PLUS, c("3"), MINUS); } @Test(expected = UnmatchedBracketsException.class) public void testUnmatchedBrackets() { given(leftBracket("["), dec("2"), rightBracket("}")); } @Test(expected = UnmatchedBracketsException.class) public void testUnmatchedBracketsOnUnaryFunction() { given(symbol("a"), leftBracket("("), dec("2"), rightBracket("}")); } @Test(expected = UnmatchedBracketsException.class) public void testUnmatchedBracketsOnBinaryFunction() { given(symbol("b"), leftBracket("("), dec("2"), COMMA, dec("3"), rightBracket("]")); } @Test(expected = UnfinishedExpressionException.class) public void testUnclosedBracket() { given(LEFT_BRACKET, dec("2")); } @Test(expected = UnfinishedExpressionException.class) public void testUnclosedBracketWithComma() { given(LEFT_BRACKET, dec("2"), COMMA, dec("3")); } @Test public void testSymbolGet() { // pi given(symbol("pi")) .expect(get("pi")); // pi + 2 given(symbol("pi"), OP_PLUS, dec("2")) .expect(get("pi"), c("2"), PLUS); // 2 + pi given(dec("2"), OP_PLUS, symbol("pi")) .expect(c("2"), get("pi"), PLUS); // a + b given(symbol("a"), OP_PLUS, symbol("b")) .expect(get("a"), get("b"), PLUS); // sin(a) given(symbol("sin"), LEFT_BRACKET, symbol("a"), RIGHT_BRACKET) .expect(get("a"), call("sin", 1)); } @Test public void testNullaryFunction() { // test() given(symbol("test"), LEFT_BRACKET, RIGHT_BRACKET) .expect(call("test", 0)); // p() + e() given(symbol("pi"), LEFT_BRACKET, RIGHT_BRACKET, OP_PLUS, symbol("e"), LEFT_BRACKET, RIGHT_BRACKET) .expect(call("pi", 0), call("e", 0), PLUS); } @Test public void testUnaryFunction() { // sin(2) given(symbol("sin"), LEFT_BRACKET, dec("2"), RIGHT_BRACKET) .expect(c("2"), call("sin", 1)); // sin((2)) given(symbol("sin"), LEFT_BRACKET, LEFT_BRACKET, dec("2"), RIGHT_BRACKET, RIGHT_BRACKET) .expect(c("2"), call("sin", 1)); // sin(2 + 3) given(symbol("sin"), LEFT_BRACKET, dec("2"), OP_PLUS, dec("3"), RIGHT_BRACKET) .expect(c("2"), c("3"), PLUS, call("sin", 1)); // sin((1)) given(symbol("sin"), LEFT_BRACKET, LEFT_BRACKET, dec("1"), RIGHT_BRACKET, RIGHT_BRACKET) .expect(c("1"), call("sin", 1)); } @Test public void testBinaryFunction() { // exp(2, 3) given(symbol("exp"), LEFT_BRACKET, dec("2"), COMMA, dec("3"), RIGHT_BRACKET) .expect(c("2"), c("3"), call("exp", 2)); // exp(2 + 3, 3 - 4) given(symbol("exp"), LEFT_BRACKET, dec("2"), OP_PLUS, dec("3"), COMMA, dec("4"), OP_MINUS, dec("5"), RIGHT_BRACKET) .expect(c("2"), c("3"), PLUS, c("4"), c("5"), MINUS, call("exp", 2)); // exp((2), 3) given(symbol("exp"), LEFT_BRACKET, LEFT_BRACKET, dec("2"), RIGHT_BRACKET, COMMA, dec("3"), RIGHT_BRACKET) .expect(c("2"), c("3"), call("exp", 2)); } @Test public void testFunctionSum() { // sin(2) + cos(3) given(symbol("sin"), LEFT_BRACKET, dec("2"), RIGHT_BRACKET, OP_PLUS, symbol("cos"), LEFT_BRACKET, dec("3"), RIGHT_BRACKET) .expect(c("2"), call("sin", 1), c("3"), call("cos", 1), PLUS); // sin(2) + exp(3, 4) given(symbol("sin"), LEFT_BRACKET, dec("2"), RIGHT_BRACKET, OP_PLUS, symbol("exp"), LEFT_BRACKET, dec("3"), COMMA, dec("4"), RIGHT_BRACKET) .expect(c("2"), call("sin", 1), c("3"), c("4"), call("exp", 2), PLUS); } @Test public void testNestedFunctions() { // exp(pi, e) given(symbol("exp"), LEFT_BRACKET, symbol("pi"), COMMA, symbol("e"), RIGHT_BRACKET) .expect(get("pi"), get("e"), call("exp", 2)); // sin(cos(3)) given(symbol("sin"), LEFT_BRACKET, symbol("cos"), LEFT_BRACKET, dec("3"), RIGHT_BRACKET, RIGHT_BRACKET) .expect(c("3"), call("cos", 1), call("sin", 1)); // exp(sin(3), 4 + cos(2)) given(symbol("exp"), LEFT_BRACKET, symbol("sin"), LEFT_BRACKET, dec("3"), RIGHT_BRACKET, COMMA, dec("4"), OP_PLUS, symbol("cos"), LEFT_BRACKET, dec("2"), RIGHT_BRACKET, RIGHT_BRACKET) .expect(c("3"), call("sin", 1), c("4"), c("2"), call("cos", 1), PLUS, call("exp", 2)); } @Test public void testUnaryOperators() { // -2 given(OP_MINUS, dec("2")) .expect(c("2"), UNARY_MINUS); // !3 given(OP_NEG, dec("3")) .expect(c("3"), UNARY_NEG); // 1 - -2 given(dec("1"), OP_MINUS, OP_MINUS, dec("2")) .expect(c("1"), c("2"), UNARY_MINUS, MINUS); // 1 * -2 given(dec("1"), OP_MULTIPLY, OP_MINUS, dec("2")) .expect(c("1"), c("2"), UNARY_MINUS, MULTIPLY); // 1 * (-2) given(dec("1"), OP_MULTIPLY, LEFT_BRACKET, OP_MINUS, dec("2"), RIGHT_BRACKET) .expect(c("1"), c("2"), UNARY_MINUS, MULTIPLY); // (2) - 3 given(LEFT_BRACKET, dec("2"), RIGHT_BRACKET, OP_MINUS, dec("3")) .expect(c("2"), c("3"), MINUS); // -(2) given(OP_MINUS, LEFT_BRACKET, dec("2"), RIGHT_BRACKET) .expect(c("2"), UNARY_MINUS); // -(2 - 3) given(OP_MINUS, LEFT_BRACKET, dec("2"), OP_MINUS, dec("3"), RIGHT_BRACKET) .expect(c("2"), c("3"), MINUS, UNARY_MINUS); // -pi given(OP_MINUS, symbol("pi")) .expect(get("pi"), UNARY_MINUS); // sin(-2) given(symbol("sin"), LEFT_BRACKET, OP_MINUS, dec("2"), RIGHT_BRACKET) .expect(c("2"), UNARY_MINUS, call("sin", 1)); // -sin(2) given(OP_MINUS, symbol("sin"), LEFT_BRACKET, dec("2"), RIGHT_BRACKET) .expect(c("2"), call("sin", 1), UNARY_MINUS); } @Test public void testDoubleUnaryOperators() { // --2 given(OP_MINUS, OP_MINUS, dec("2")) .expect(c("2"), UNARY_MINUS, UNARY_MINUS); // -+2 given(OP_MINUS, OP_PLUS, dec("2")) .expect(c("2"), UNARY_PLUS, UNARY_MINUS); // -!2 given(OP_MINUS, OP_NEG, dec("2")) .expect(c("2"), UNARY_NEG, UNARY_MINUS); // !-+2 given(OP_NEG, OP_MINUS, OP_PLUS, dec("2")) .expect(c("2"), UNARY_PLUS, UNARY_MINUS, UNARY_NEG); } @Test public void testDefaultOperator() { // 2a == 2 * a given(dec("2"), symbol("a")) .expect(c("2"), get("a"), DEFAULT) .expectSameAs(dec("2"), OP_DEFAULT, symbol("a")); // -2a == -2 * a given(OP_MINUS, dec("2"), symbol("a")) .expect(c("2"), UNARY_MINUS, get("a"), DEFAULT) .expectSameAs(OP_MINUS, dec("2"), OP_DEFAULT, symbol("a")); // -2a() == -2 * a() given(OP_MINUS, dec("2"), symbol("a"), LEFT_BRACKET, RIGHT_BRACKET) .expect(c("2"), UNARY_MINUS, call("a", 0), DEFAULT) .expectSameAs(OP_MINUS, dec("2"), OP_DEFAULT, symbol("a"), LEFT_BRACKET, RIGHT_BRACKET); // 2(a) = 2 * (a) given(dec("2"), LEFT_BRACKET, symbol("a"), RIGHT_BRACKET) .expect(c("2"), get("a"), DEFAULT) .expectSameAs(dec("2"), OP_DEFAULT, symbol("a")); // 3 a + 2 = 3 * a + 2 given(dec("3"), symbol("a"), OP_PLUS, dec("2")) .expect(c("3"), get("a"), DEFAULT, c("2"), PLUS); // 3 + 2a == 3 + 2 * a given(dec("3"), OP_PLUS, dec("2"), symbol("a")) .expect(c("3"), c("2"), get("a"), DEFAULT, PLUS) .expectSameAs(dec("3"), OP_PLUS, dec("2"), OP_DEFAULT, symbol("a")); // 3a(2) == 3 * a(2) given(dec("3"), symbol("a"), LEFT_BRACKET, dec("2"), RIGHT_BRACKET) .expect(c("3"), c("2"), call("a", 1), DEFAULT) .expectSameAs(dec("3"), OP_DEFAULT, symbol("a"), LEFT_BRACKET, dec("2"), RIGHT_BRACKET); // 2 3 = 2 * 3 (weird edge condition, but whatever) given(dec("2"), dec("3")) .expect(c("2"), c("3"), DEFAULT) .expectSameAs(dec("2"), OP_DEFAULT, dec("3")); // 3(2) == 3 * 2 given(dec("3"), LEFT_BRACKET, dec("2"), RIGHT_BRACKET) .expect(c("3"), c("2"), DEFAULT) .expectSameAs(dec("3"), OP_DEFAULT, dec("2")); // (2)3 = 2 * 3 given(LEFT_BRACKET, dec("2"), RIGHT_BRACKET, dec("3")) .expect(c("2"), c("3"), DEFAULT) .expectSameAs(dec("2"), OP_DEFAULT, dec("3")); // (2) i == 2 * i given(LEFT_BRACKET, dec("2"), RIGHT_BRACKET, symbol("i")) .expect(c("2"), get("i"), DEFAULT) .expectSameAs(dec("2"), OP_DEFAULT, symbol("i")); // -(2) i == -2 * i given(OP_MINUS, LEFT_BRACKET, dec("2"), RIGHT_BRACKET, symbol("i")) .expect(c("2"), UNARY_MINUS, get("i"), DEFAULT) .expectSameAs(OP_MINUS, dec("2"), OP_DEFAULT, symbol("i")); // a(2i, 4) == a(2*i, 4) given(symbol("a"), LEFT_BRACKET, dec("2"), symbol("i"), COMMA, dec("4"), RIGHT_BRACKET) .expect(c("2"), get("i"), DEFAULT, c("4"), call("a", 2)) .expectSameAs(symbol("a"), LEFT_BRACKET, dec("2"), OP_DEFAULT, symbol("i"), COMMA, dec("4"), RIGHT_BRACKET); // (2)(3) == 2 * 3 given(LEFT_BRACKET, dec("2"), RIGHT_BRACKET, LEFT_BRACKET, dec("3"), RIGHT_BRACKET) .expect(c("2"), c("3"), DEFAULT) .expectSameAs(dec("2"), OP_DEFAULT, dec("3")); // (2)5(3) == 2 * 5 * 3 given(LEFT_BRACKET, dec("2"), RIGHT_BRACKET, dec("5"), LEFT_BRACKET, dec("3"), RIGHT_BRACKET) .expect(c("2"), c("5"), DEFAULT, c("3"), DEFAULT) .expectSameAs(dec("2"), OP_DEFAULT, dec("5"), OP_DEFAULT, dec("3")); // (2)a(3) = 2 * a(3) given(LEFT_BRACKET, dec("2"), RIGHT_BRACKET, symbol("a"), LEFT_BRACKET, dec("3"), RIGHT_BRACKET) .expect(c("2"), c("3"), call("a", 1), DEFAULT) .expectSameAs(dec("2"), OP_DEFAULT, symbol("a"), LEFT_BRACKET, dec("3"), RIGHT_BRACKET); // a(3)2 = a(3) * 2 given(symbol("a"), LEFT_BRACKET, dec("3"), RIGHT_BRACKET, dec("2")) .expect(c("3"), call("a", 1), c("2"), DEFAULT) .expectSameAs(symbol("a"), LEFT_BRACKET, dec("3"), RIGHT_BRACKET, OP_DEFAULT, dec("2")); // a(3)j = a(3) * j given(symbol("a"), LEFT_BRACKET, dec("3"), RIGHT_BRACKET, symbol("j")) .expect(c("3"), call("a", 1), get("j"), DEFAULT) .expectSameAs(symbol("a"), LEFT_BRACKET, dec("3"), RIGHT_BRACKET, OP_DEFAULT, symbol("j")); // a(3)(2) = a(3) * 2 given(symbol("a"), LEFT_BRACKET, dec("3"), RIGHT_BRACKET, LEFT_BRACKET, dec("2"), RIGHT_BRACKET) .expect(c("3"), call("a", 1), c("2"), DEFAULT) .expectSameAs(symbol("a"), LEFT_BRACKET, dec("3"), RIGHT_BRACKET, OP_DEFAULT, dec("2")); // a[3] = a * [3] given(symbol("a"), leftBracket("["), dec("3"), rightBracket("]")) .expect(get("a"), c("3"), DEFAULT) .expectSameAs(symbol("a"), OP_DEFAULT, dec("3")); // a 3 = a * 3 given(symbol("a"), dec("3")) .expect(get("a"), c("3"), DEFAULT) .expectSameAs(symbol("a"), OP_DEFAULT, dec("3")); // a b = a * b given(symbol("a"), symbol("b")) .expect(get("a"), get("b"), DEFAULT) .expectSameAs(symbol("a"), OP_DEFAULT, symbol("b")); // -a b = -a * b given(OP_MINUS, symbol("a"), symbol("b")) .expect(get("a"), UNARY_MINUS, get("b"), DEFAULT) .expectSameAs(OP_MINUS, symbol("a"), OP_DEFAULT, symbol("b")); } @Test public void testValueModifierQuote() { // #12 given(QUOTE_MODIFIER, dec("12")).expect(OPEN_ROOT_QUOTE_M, valueMarker("12"), CLOSE_ROOT_QUOTE_M); // #"abc" given(QUOTE_MODIFIER, string("abc")).expect(OPEN_ROOT_QUOTE_M, valueMarker("abc"), CLOSE_ROOT_QUOTE_M); } @Test public void testValueSymbolQuote() { // quote(12) given(QUOTE_SYMBOL, LEFT_BRACKET, dec("12"), RIGHT_BRACKET).expect(OPEN_ROOT_QUOTE_S, valueMarker("12"), CLOSE_ROOT_QUOTE_S); // quote("abc") given(QUOTE_SYMBOL, LEFT_BRACKET, string("abc"), RIGHT_BRACKET).expect(OPEN_ROOT_QUOTE_S, valueMarker("abc"), CLOSE_ROOT_QUOTE_S); } @Test public void testRawValueQuote() { // #abc given(QUOTE_MODIFIER, symbol("abc")).expect(OPEN_ROOT_QUOTE_M, rawValueMarker(symbol("abc")), CLOSE_ROOT_QUOTE_M); // #+ given(QUOTE_MODIFIER, OP_PLUS).expect(OPEN_ROOT_QUOTE_M, rawValueMarker(OP_PLUS), CLOSE_ROOT_QUOTE_M); // #. given(QUOTE_MODIFIER, mod(".")).expect(OPEN_ROOT_QUOTE_M, rawValueMarker(mod(".")), CLOSE_ROOT_QUOTE_M); } @Test public void testQuoteAndDeafultOpOrder() { // #abc(12) given(QUOTE_MODIFIER, symbol("abc"), LEFT_BRACKET, dec("12"), RIGHT_BRACKET) .expectSameAs(QUOTE_MODIFIER, symbol("abc"), OP_DEFAULT, LEFT_BRACKET, dec("12"), RIGHT_BRACKET) .expect(OPEN_ROOT_QUOTE_M, rawValueMarker(symbol("abc")), CLOSE_ROOT_QUOTE_M, c("12"), DEFAULT); } }