using System.Linq.Expressions;
public static void Main()
var expr = QueryGrammar<StoreData>.ParseCondition(@"
EQUAL(timestamp, 123456789)
var func = expr.Compile();
var result = func(new StoreData
Console.WriteLine(result);
public string id { get; set; }
public string title { get; set; }
public string content { get; set; }
public int views { get; set; }
public int timestamp { set; get; }
public static class QueryGrammar<T>
static readonly ParameterExpression Param = Expression.Parameter(typeof(T));
public static Expression<Func<T, bool>> ParseCondition(string input)
.Select(body => Expression.Lambda<Func<T, bool>>(body, Param))
static Parser<char> EscapingChar => Parse.Char('\\');
static Parser<char> EscapedChar =>
static Parser<char> QuoteChar => Parse.Char('"');
static Parser<char> NonQuoteChar =>
Parse.AnyChar.Except(QuoteChar);
static Parser<string> QuotedText =>
from content in EscapedChar.Or(NonQuoteChar).Many().Text()
private static Expression GetProperty(string propName)
var memInfo = typeof(T).GetMember(propName,
BindingFlags.Instance | BindingFlags.Public)
throw new ParseException($"Property with name '{propName}' not found on type '{typeof(T)}'");
return Expression.MakeMemberAccess(Param, memInfo);
static Parser<Expression> FieldToken =>
Parse.Letter.AtLeastOnce().Text().Token()
.Select(name => GetProperty(name));
static Parser<Expression> ValueToken =>
(from strNum in Parse.Number
select Expression.Constant(int.Parse(strNum)))
.Or(QuotedText.Select(t => Expression.Constant(t)))
static Parser<Expression> Term =>
static Parser<Expression> EqualTerm =>
(from tuple in Comparer("EQUAL")
select Expression.Equal(tuple.Field, tuple.Value))
static Parser<Expression> GreaterThanTerm =>
(from tuple in Comparer("GREATER_THAN")
select Expression.GreaterThan(tuple.Field, tuple.Value))
static Parser<(Expression Field, Expression Value)> Comparer(string comparison) =>
(from startsWith in Parse.String(comparison).Text().Token()
from openParen in Parse.Char('(')
from comma in Parse.Char(',')
from closeParen in Parse.Char(')')
static Parser<Expression> AndTerm =>
(from tuple in Combinator("AND")
select Expression.AndAlso(tuple.Left, tuple.Right))
static Parser<Expression> OrTerm =>
(from tuple in Combinator("OR")
select Expression.OrElse(tuple.Left, tuple.Right))
static Parser<(Expression Left, Expression Right)> Combinator(string combination) =>
(from startsWith in Parse.String(combination).Text().Token()
from openParen in Parse.Char('(')
from comma in Parse.Char(',')
from closeParen in Parse.Char(')')