为了账号安全,请及时绑定邮箱和手机立即绑定

Sprache 中的文本查询解析

Sprache 中的文本查询解析

C#
人到中年有点甜 2022-07-23 08:56:02
我正在尝试编写一些代码来匹配基于模式的字符串:模式:“狗和(猫或山羊)”测试字符串:“doggoat” 结果:true测试字符串:“dogfrog” 结果:假我正在尝试使用 Sprache 编写解析器,其中大部分逻辑由 Corey对类似问题的出色回答提供。我快到了,但是运行代码时出现异常:System.Func'没有为类型2[System.String,System.Boolean]' 和 ''System.Func`2[System.String,System.Boolean]'定义二元运算符 AndAlso 。我知道这意味着我需要将表达式树节点处的 lambda 表达式与逻辑运算符结合起来,我根据此处另一个问题的答案尝试使用 ExpressionVisitor 。但是,程序在执行 ExpressionVisitor 之前崩溃 - 似乎首先执行 Parse 命令,但我不太明白为什么(可能是因为 Sprache.Parse.Select 语句不强制执行 lambda?) ,或者如何强制它先被执行。示例代码如下(为了简洁起见,我删除了所有运算符,但“和”除外,从Corey 的模板中重新引入它们是微不足道的。必须从 NuGet 添加 Sprache 才能编译代码。class Program{    static void Main(string[] args)    {        var patternString = "dog and cat";        var strTest = "dog cat";        var strTest2 = "dog frog";        var conditionTest = ConditionParser.ParseCondition(patternString);        var fnTest = conditionTest.Compile();        bool res1 = fnTest(strTest); //true        bool res2 = fnTest(strTest2); //false    }}public static class ConditionParser{    static ParameterExpression Param = Expression.Parameter(typeof(string), "_");    public static Expression<Func<string, bool>> ParseCondition(string text)    {        return Lambda.Parse(text);    }    private static Parser<Expression<Func<string, bool>>> Lambda    {        get        {            var reduced = AndTerm.End().Select(delegate (Expression body)            {                var replacer = new ParameterReplacer(Param);                return Expression.Lambda<Func<string, bool>>((BinaryExpression)replacer.Visit(body), Param);            });            return reduced;        }    }    static Parser<Expression> AndTerm =>        Parse.ChainOperator(OpAnd, StringMatch, Expression.MakeBinary);    // Other operators (or, not etc.) can be chained here, between AndTerm and StringMatch    static Parser<ExpressionType> OpAnd = MakeOperator("and", ExpressionType.AndAlso);    private static Parser<Expression> StringMatch =>        Parse.Letter.AtLeastOnce()        .Text().Token()        .Select(value => StringContains(value));
查看完整描述

1 回答

?
犯罪嫌疑人X

TA贡献2080条经验 获得超4个赞

您的代码存在几个问题,但导致异常的主要问题是StringContains返回 lambda 表达式的方法。并且Expression.AndAlso(以及大多数Expression方法)基于简单的非 lambda 表达式(或 lambda 表达式主体)。解析代码的整个想法是识别和组合简单的表达式,并从结果表达式中生成单个 lambda 表达式。

要解决原始问题,该StringContains方法应直接返回MethodCall表达式而不是 lambda 表达式。

同一StringContains方法中的第二个问题是将参数反转为string.Contains. 它基本上是这样做token.Contains(parameter)的,而根据预期结果它应该做相反的事情。

整个方法(使用另一个方便的Expression.Call重载)可以简化为

static Expression StringContains(string subString) =>
    Expression.Call(Param, "Contains", Type.EmptyTypes, Expression.Constant(subString));

现在一切都应该按预期工作。

但是,由于ConditionParser该类使用单个ParameterExpression实例,然后用于构建 lambda 表达式,因此不需要ParameterReplacer,因此Lambda方法(属性)可以简化为

private static Parser<Expression<Func<string, bool>>> Lambda =>
    AndTerm.End().Select(body => Expression.Lambda<Func<string, bool>>(body, Param));


查看完整回答
反对 回复 2022-07-23
  • 1 回答
  • 0 关注
  • 163 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号