目的
之前就考虑评估过 函数式解析器 在dotnet这些面向对象语言上有着一些损耗,虽然表意性很强,与ABNF范式结合使用,维护性大大提升
不过由于性能考虑(以及之前认为也或许没有太多机会实现解析器),就没打算继续深究
不过现实确实有多次需要实现解析器,每次从0手写,太费时间了,比较很多实现对于解析性能并不需极致性能,(特别现在ai code疯狂的时代,老板哪会给那么多时间让你慢慢扣性能,老板们都常说ai几秒钟的事情,你个辣鸡还花几天)
所以为了血压和身体健康,只要性能将就,还是对于解析不太在意性能的场景以后就这样偷懒吧 (比如今年搞得 VKProxy 里面得动态条件和简单模板替换, 路子可行也许后面还是换掉吧,维护多累呀)
当然也有高手搞过类似的库,比如 Parlot , 不过其不支持 stream, 理想情况还是想stream 也能支持, 所以现在先简单搞一波:参考 Parlot 简单实现 string 解析器底层以及 json path 简单版
然后和 Newtonsoft.json 在简单的json path 场景比较,如果打不过,那怕没有继续的必要了
简要说明函数式解析器
这里以最简单 json path "$.Name" 举例,
我们所需要解析即为 $ 开头, . 之后字符为属性名
换成代码大致为 Parser = Char('$').And(Char('.')).And(AnyExclude("[]().,\" '\r\n@$!= new WildcardSelectorStatment()).Name(nameof(WildcardSelector)); public static readonly Parser IndexSelector = Int.Then(static x => new IndexSelectorStatment() { Index = x }).Name(nameof(IndexSelector)); public static readonly Parser StringLiteral = Between(DoubleQuoted, ZeroOrOne(Any("\"", mustHasEnd: true, escape: '\\')), DoubleQuoted).Or(Between(SingleQuoted, ZeroOrOne(Any("'", mustHasEnd: true, escape: '\\')), SingleQuoted)).Name(nameof(StringLiteral)); public static readonly Parser NameSelector = StringLiteral.Then(static x => new Member() { Name = x.Span.ToString() }).Name(nameof(NameSelector)); public static readonly Parser Start = Int; public static readonly Parser End = Int; public static readonly Parser Step = Int; public static readonly Parser S = IgnoreChar(new char[] { (char)0x20, // Space (char)0x09, //Horizontal tab (char)0x0A, // Line feed or New line (char)0x0D // Carriage return }).Name(nameof(S)); public static readonly Parser CurrentNodeIdentifier = Char('@').Name(nameof(CurrentNodeIdentifier)); public static readonly Parser LogicalNotOp = Char('!').Name(nameof(LogicalNotOp)); public static readonly Parser ComparisonOp = Text("==").Or(Text("!=")).Or(Text("=")).Or(Text("")).Name(nameof(ComparisonOp)); public static readonly Parser Num = Decimal(NumberOptions.Float).Then(static x => new NumberValue(x)).Name(nameof(Num)); public static readonly Parser True = Text("true").Then(static x => BoolValue.True).Name(nameof(True)); public static readonly Parser False = Text("false").Then(static x => BoolValue.False).Name(nameof(False)); public static readonly Parser Null = Text("null").Then(static x => NullValue.Value).Name(nameof(Null)); private const string name = "[]().,\" '\r\n@$!= |