gpt4 book ai didi

c# - Sprache 中的文本查询解析

转载 作者:太空宇宙 更新时间:2023-11-03 22:37:35 25 4
gpt4 key购买 nike

我正在尝试编写一些代码来根据模式匹配字符串:

模式:“狗和(猫或山羊)”

测试字符串:“doggoat” 结果:true

测试字符串:“dogfrog” 结果:false

我正在尝试使用 Sprache 编写解析器,其中大部分逻辑由 Corey 的 excellent answer 提供到类似的问题。我快到了,但是在运行代码时出现异常:

'The binary operator AndAlso is not defined for the types System.Func2[System.String,System.Boolean]' and ''System.Func`2[System.String,System.Boolean]'.'

我明白这意味着我需要将表达式树节点处的 lambda 与逻辑运算符结合起来,我已尝试使用基于另一个问题 here 的答案的 ExpressionVisitor .但是,程序在执行 ExpressionVisitor 之前就崩溃了——似乎先执行了 Parse 命令,但我不太明白为什么(也许是因为 Sprache.Parse.Select 语句不强制执行 lambda?) ,或者如何强制先执行它。
示例代码如下(为了简洁起见,我去掉了所有运算符,但去掉了“和”,从 Corey's template 重新引入它们很简单。必须从 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));

static Expression StringContains(string subString)
{
MethodInfo contains = typeof(string).GetMethod("Contains");

var call = Expression.Call(
Expression.Constant(subString),
contains,
Param
);

var ret = Expression.Lambda<Func<string, bool>>(call, Param);
return ret;
}

// Helper: define an operator parser
static Parser<ExpressionType> MakeOperator(string token, ExpressionType type)
=> Parse.IgnoreCase(token).Token().Return(type);
}

internal class ParameterReplacer : ExpressionVisitor
{
private readonly ParameterExpression _parameter;

protected override Expression VisitParameter(ParameterExpression node)
{
return base.VisitParameter(_parameter);
}

internal ParameterReplacer(ParameterExpression parameter)
{
_parameter = parameter;
}
}

最佳答案

您的代码存在多个问题,但导致相关异常的主要问题是返回 lambda 表达式的 StringContains 方法。而 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));

关于c# - Sprache 中的文本查询解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54215088/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com