public void ExpressionWithNestedMethodCalls_ReturnsValidTokens()
        {
            string input = "@.call(@.a.SubString(@.a.b.GetLength(@.a.c.Something())))";

            IReadOnlyList <FilterExpressionToken> tokens = FilterExpressionTokenizer.Tokenize(input);

            Assert.Single(tokens);
            Assert.IsType <MethodCallExpressionToken>(tokens[0]);

            MethodCallExpressionToken mct = tokens[0] as MethodCallExpressionToken;

            Assert.Single(mct.Arguments);
            Assert.IsType <MethodCallExpressionToken>(mct.Arguments.Single());
            Assert.IsType <PropertyExpressionToken>(mct.CalledOnExpression);

            MethodCallExpressionToken arg = mct.Arguments[0] as MethodCallExpressionToken;

            Assert.Single(arg.Arguments);
            Assert.IsType <MethodCallExpressionToken>(arg.Arguments[0]);
            Assert.IsType <PropertyExpressionToken>(arg.CalledOnExpression);

            MethodCallExpressionToken innerArg = arg.Arguments[0] as MethodCallExpressionToken;

            Assert.Single(innerArg.Arguments);
            Assert.IsType <MethodCallExpressionToken>(innerArg.Arguments[0]);
            Assert.IsType <PropertyExpressionToken>(innerArg.CalledOnExpression);

            MethodCallExpressionToken innerInnerArg = innerArg.Arguments[0] as MethodCallExpressionToken;

            Assert.Empty(innerInnerArg.Arguments);
            Assert.IsType <PropertyExpressionToken>(innerInnerArg.CalledOnExpression);
        }
        public void ExpressionWithMethodCallWithMultipleArguments_ReturnsValidTokens()
        {
            string input = "'a'.ToUpper('abc', 123, true, @.CallingMethod())";
            IReadOnlyList <FilterExpressionToken> tokens = FilterExpressionTokenizer.Tokenize(input);

            Assert.Single(tokens);
            Assert.IsType <MethodCallExpressionToken>(tokens.Single());

            MethodCallExpressionToken mct = tokens.Single() as MethodCallExpressionToken;

            Assert.IsType <ConstantStringExpressionToken>(mct.CalledOnExpression);
            Assert.Equal(4, mct.Arguments.Length);

            Assert.IsType <ConstantStringExpressionToken>(mct.Arguments[0]);
            Assert.Equal("abc", (mct.Arguments[0] as ConstantStringExpressionToken).StringValue);

            Assert.IsType <ConstantNumberExpressionToken>(mct.Arguments[1]);
            Assert.Equal(123.0, (mct.Arguments[1] as ConstantNumberExpressionToken).Token.NumberValue, 6);

            Assert.IsType <ConstantBoolExpressionToken>(mct.Arguments[2]);
            Assert.True((mct.Arguments[2] as ConstantBoolExpressionToken).Token.BoolValue);

            Assert.IsType <MethodCallExpressionToken>(mct.Arguments[3]);
            MethodCallExpressionToken innerMct = mct.Arguments[3] as MethodCallExpressionToken;

            Assert.IsType <PropertyExpressionToken>(innerMct.CalledOnExpression);
            Assert.Empty((innerMct.CalledOnExpression as PropertyExpressionToken).PropertyChain);
            Assert.Equal("CallingMethod", innerMct.MethodName);
        }
        public void ExpressionWithChainedMethodCalls_ReturnValidToken()
        {
            string input = "'a'.ToUpper0().ToUpper1(1).ToUpper2(7, 9.12).ToUpper3('abc', true, false, false, 'A') == \"A\"";
            IReadOnlyList <FilterExpressionToken> tokens = FilterExpressionTokenizer.Tokenize(input);

            Assert.Equal(3, tokens.Count);
            Assert.IsType <MethodCallExpressionToken>(tokens.First());

            MethodCallExpressionToken toUpper3 = tokens.First() as MethodCallExpressionToken;
            MethodCallExpressionToken toUpper2 = toUpper3.CalledOnExpression as MethodCallExpressionToken;
            MethodCallExpressionToken toUpper1 = toUpper2.CalledOnExpression as MethodCallExpressionToken;
            MethodCallExpressionToken toUpper0 = toUpper1.CalledOnExpression as MethodCallExpressionToken;

            Assert.Equal("ToUpper0", toUpper0.MethodName);
            Assert.Equal("ToUpper1", toUpper1.MethodName);
            Assert.Equal("ToUpper2", toUpper2.MethodName);
            Assert.Equal("ToUpper3", toUpper3.MethodName);

            Assert.Empty(toUpper0.Arguments);

            Assert.Single(toUpper1.Arguments);
            Assert.Equal(1.0, (toUpper1.Arguments[0] as ConstantNumberExpressionToken).Token.NumberValue, 6);

            Assert.Equal(2, toUpper2.Arguments.Length);
            Assert.Equal(7.0, (toUpper2.Arguments[0] as ConstantNumberExpressionToken).Token.NumberValue, 6);
            Assert.Equal(9.12, (toUpper2.Arguments[1] as ConstantNumberExpressionToken).Token.NumberValue, 6);

            Assert.Equal(5, toUpper3.Arguments.Length);
            Assert.Equal("abc", (toUpper3.Arguments[0] as ConstantStringExpressionToken).Token.StringValue);
            Assert.True((toUpper3.Arguments[1] as ConstantBoolExpressionToken).Token.BoolValue);
            Assert.False((toUpper3.Arguments[2] as ConstantBoolExpressionToken).Token.BoolValue);
            Assert.False((toUpper3.Arguments[3] as ConstantBoolExpressionToken).Token.BoolValue);
            Assert.Equal("A", (toUpper3.Arguments[4] as ConstantStringExpressionToken).Token.StringValue);
        }
        public void ValidExpression_ReturnsValidTokens()
        {
            string input = "@.price >= 0 && (@.name.first.contains('a') || @['name'].contains(5) || [email protected])";

            IReadOnlyList <FilterExpressionToken> tokens = FilterExpressionTokenizer.Tokenize(input);

            Assert.Equal(12, tokens.Count);

            Assert.IsType <PropertyExpressionToken>(tokens[0]);
            Assert.IsType <ComparisonOperatorExpressionToken>(tokens[1]);
            Assert.IsType <ConstantNumberExpressionToken>(tokens[2]);
            Assert.IsType <LogicalBinaryOperatorExpressionToken>(tokens[3]);
            Assert.IsType <OpenGroupToken>(tokens[4]);

            Assert.IsType <MethodCallExpressionToken>(tokens[5]);
            MethodCallExpressionToken mct = tokens[5] as MethodCallExpressionToken;

            Assert.Single(mct.Arguments);
            Assert.IsType <ConstantStringExpressionToken>(mct.Arguments.Single());
            Assert.Equal("a", (mct.Arguments.Single() as ConstantStringExpressionToken).StringValue);

            Assert.IsType <LogicalBinaryOperatorExpressionToken>(tokens[6]);
            Assert.IsType <MethodCallExpressionToken>(tokens[7]);
            Assert.IsType <LogicalBinaryOperatorExpressionToken>(tokens[8]);
            Assert.IsType <NegationExpressionToken>(tokens[9]);
            Assert.IsType <PropertyExpressionToken>(tokens[10]);
            Assert.IsType <CloseGroupToken>(tokens[11]);
        }
        public void ExpressionWithMethodCallOnRoot_HasEmptyPropertyChain()
        {
            string input = "@.Something()";
            IReadOnlyList <FilterExpressionToken> tokens = FilterExpressionTokenizer.Tokenize(input);

            Assert.Single(tokens);
            Assert.IsType <MethodCallExpressionToken>(tokens.Single());
            MethodCallExpressionToken mct = tokens.Single() as MethodCallExpressionToken;

            Assert.Equal("Something", mct.MethodName);
            Assert.Empty(mct.Arguments);
            Assert.IsType <PropertyExpressionToken>(mct.CalledOnExpression);
            PropertyExpressionToken callee = mct.CalledOnExpression as PropertyExpressionToken;

            Assert.Empty(callee.PropertyChain);
        }
        public void ExpressionWithMethodCallOnStringConstant_ReturnsValidTokens()
        {
            string input = "'a'.ToUpper() == 'A'";
            IReadOnlyList <FilterExpressionToken> tokens = FilterExpressionTokenizer.Tokenize(input);

            Assert.Equal(3, tokens.Count);
            Assert.IsType <MethodCallExpressionToken>(tokens[0]);

            MethodCallExpressionToken mct = tokens[0] as MethodCallExpressionToken;

            Assert.Empty(mct.Arguments);
            Assert.IsType <ConstantStringExpressionToken>(mct.CalledOnExpression);
            ConstantStringExpressionToken constant = mct.CalledOnExpression as ConstantStringExpressionToken;

            Assert.Equal("a", constant.StringValue);
        }
        public void ExpressionWithMethodCallWithMixedArgs_ReturnsValidTokens()
        {
            string input = "@.a.c.Call1(@.call2(123, false).call3(), true)";

            IReadOnlyList <FilterExpressionToken> tokens = FilterExpressionTokenizer.Tokenize(input);

            Assert.Single(tokens);
            Assert.IsType <MethodCallExpressionToken>(tokens.Single());

            MethodCallExpressionToken call1 = tokens.Single() as MethodCallExpressionToken;

            Assert.IsType <PropertyExpressionToken>(call1.CalledOnExpression);
            PropertyExpressionToken prop1 = call1.CalledOnExpression as PropertyExpressionToken;

            Assert.Equal(2, prop1.PropertyChain.Length);
            Assert.Equal("a", prop1.PropertyChain[0].StringValue);
            Assert.Equal("c", prop1.PropertyChain[1].StringValue);
            Assert.Equal("Call1", call1.MethodName);

            Assert.Equal(2, call1.Arguments.Length);
            Assert.IsType <ConstantBoolExpressionToken>(call1.Arguments.Last());
            Assert.True(call1.Arguments.Last() is ConstantBoolExpressionToken cbt && cbt.Token.BoolValue);

            Assert.IsType <MethodCallExpressionToken>(call1.Arguments.First());
            MethodCallExpressionToken call3 = call1.Arguments.First() as MethodCallExpressionToken;

            Assert.Equal("call3", call3.MethodName);
            Assert.Empty(call3.Arguments);

            Assert.IsType <MethodCallExpressionToken>(call3.CalledOnExpression);
            MethodCallExpressionToken call2 = call3.CalledOnExpression as MethodCallExpressionToken;

            Assert.Equal("call2", call2.MethodName);
            Assert.IsType <PropertyExpressionToken>(call2.CalledOnExpression);
            Assert.Empty((call2.CalledOnExpression as PropertyExpressionToken).PropertyChain);
            Assert.Equal(2, call2.Arguments.Length);

            Assert.True(call2.Arguments[0] is ConstantNumberExpressionToken arg1 && arg1.Token.NumberValue == 123.0);
            Assert.True(call2.Arguments[1] is ConstantBoolExpressionToken arg2 && !arg2.Token.BoolValue);
        }