using System.Collections.Generic;
static void Main(string[] args)
var ex = Parser.getEx().Parse(@"
EQUAL(timestamp, 123456789)
var res = Calc.Run(ex, sd);
Console.WriteLine($"result {res}");
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 bool Run<T>(Parser.Ex ex, T t) {
if(ex.Pr is Parser.PrEx exPr && exPr.Ex.Length == 1) {
return Run(exPr.Ex[0], t);
throw new Exception($"invalid parameters to {ex.Op}");
if(ex.Pr is Parser.PrEx exPr && exPr.Ex.Length == 2) {
var l = Run(exPr.Ex[0], t);
var r = Run(exPr.Ex[1], t);
case "AND": return l && r;
case "OR" : return l || r;
throw new Exception($"invalid parameters to {ex.Op}");
if(ex.Pr is Parser.PrIdLt exIdLt) {
var p = tt.GetProperty(exIdLt.Id);
if(p == null) throw new Exception($"no property {exIdLt.Id} on {tt.Name}");
if(p.PropertyType == typeof(string) && exIdLt.Lt is Parser.LtQuotedString ltQuotedString){
var pval = p.GetValue(t) as string;
return pval == ltQuotedString.Val;
throw new Exception($"{ex.Op} invalid operator for string");
else if(p.PropertyType == typeof(int) && exIdLt.Lt is Parser.LtNumber ltNumber){
var pval = (int)p.GetValue(t);
if(!int.TryParse(ltNumber.Val, out lval)) throw new Exception($"{ex.Op} {exIdLt.Id} {ltNumber.Val} is not a number");
case "EQUAL" : return pval == lval;
case "GREATER_THAN": return pval > lval;
case "LESS_THAN" : return pval < lval;
throw new Exception($"{ex.Op} invalid operator for string");
throw new Exception($"{ex.Op} {exIdLt.Id} invalid type");
throw new Exception($"invalid parameters to {ex.Op}");
throw new Exception($"{ex.Op} unknown operator");
public string Op { get; set; }
public Pr Pr { get; set; }
public class PrIdLt : Pr {
public string Id { get; set; }
public Lt Lt { get; set; }
public Ex[] Ex { get; set; }
public class LtQuotedString : Lt { public string Val { get; set; } }
public class LtNumber : Lt { public string Val { get; set; } }
public static Parser<Ex> getEx(){
from p in getPr().Contained(Parse.Char('('), Parse.Char(')')).Token()
select new Ex { Op = o, Pr = p };
public static Parser<Pr> getPr(){
from c in Parse.Char(',').Token()
select new PrIdLt { Id = i, Lt = l };
from ex in Parse.Ref(() => getEx())
from em in Parse.Char(',').Token().Then(_ => Parse.Ref(() => getEx())).Many()
select new PrEx { Ex = em.Prepend(ex).ToArray() };
public static Parser<string> getOp() => Parse.Letter.Or(Parse.Char('_')).AtLeastOnce().Text().Token();
public static Parser<string> getId() => Parse.Identifier(Parse.Letter, Parse.LetterOrDigit).Token();
public static Parser<Lt> getLt(){
from open in Parse.Char('"')
from value in Parse.CharExcept('"').Many().Text()
from close in Parse.Char('"')
quoted_string.Select(a => new LtQuotedString { Val = a })
.Or<Lt>(Parse.Number.Select(a => new LtNumber(){ Val = a })).Token();