我正在尝试编写一些代码来匹配基于模式的字符串:
模式:“狗和(猫或山羊)”
测试字符串:“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));
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;
}
}