您可能需要考虑定义自己的解析器,然后遍历AST 以获得所需的值。有很多工具可以做到这一点(例如,参见 flex 或 bison)。但在 .net 世界中,Irony 可能是一个可行的选择:它在 .net standard 2.0 中可用,我在插入 .net core 2.1 控制台测试项目时没有问题。
首先,您通常需要定义一个语法。幸运的是,微软对supply us with EBNF reference 非常友好,所以我们要做的就是让它适应 Irony。我最终实现了上面语法的一个子集,它似乎可以满足您的示例语句(有点超出预期,请随意削减)。
using Irony.Parsing;
namespace irony_playground
{
[Language("OData", "1.0", "OData Filter")]
public class OData: Grammar
{
public OData()
{
// first we define some terms
var identifier = new RegexBasedTerminal("identifier", "[a-zA-Z_][a-zA-Z_0-9]*");
var string_literal = new StringLiteral("string_literal", "'");
var integer_literal = new NumberLiteral("integer_literal", NumberOptions.IntOnly);
var float_literal = new NumberLiteral("float_literal", NumberOptions.AllowSign|NumberOptions.AllowSign)
| new RegexBasedTerminal("float_literal", "(NaN)|-?(INF)");
var boolean_literal = new RegexBasedTerminal("boolean_literal", "(true)|(false)");
var filter_expression = new NonTerminal("filter_expression");
var boolean_expression = new NonTerminal("boolean_expression");
var collection_filter_expression = new NonTerminal("collection_filter_expression");
var logical_expression = new NonTerminal("logical_expression");
var comparison_expression = new NonTerminal("comparison_expression");
var variable = new NonTerminal("variable");
var field_path = new NonTerminal("field_path");
var lambda_expression = new NonTerminal("lambda_expression");
var comparison_operator = new NonTerminal("comparison_operator");
var constant = new NonTerminal("constant");
Root = filter_expression; // this is where our entry point will be.
// and from here on we expand on all terms and their relationships
filter_expression.Rule = boolean_expression;
boolean_expression.Rule = collection_filter_expression
| logical_expression
| comparison_expression
| boolean_literal
| "(" + boolean_expression + ")"
| variable;
variable.Rule = identifier | field_path;
field_path.Rule = MakeStarRule(field_path, ToTerm("/"), identifier);
collection_filter_expression.Rule =
field_path + "/all(" + lambda_expression + ")"
| field_path + "/any(" + lambda_expression + ")"
| field_path + "/any()";
lambda_expression.Rule = identifier + ":" + boolean_expression;
logical_expression.Rule =
boolean_expression + (ToTerm("and", "and") | ToTerm("or", "or")) + boolean_expression
| ToTerm("not", "not") + boolean_expression;
comparison_expression.Rule =
variable + comparison_operator + constant |
constant + comparison_operator + variable;
constant.Rule =
string_literal
| integer_literal
| float_literal
| boolean_literal
| ToTerm("null");
comparison_operator.Rule = ToTerm("gt") | "lt" | "ge" | "le" | "eq" | "ne";
RegisterBracePair("(", ")");
}
}
}
一点提示:Irony 带有 Grammar Explorer 工具,允许您加载语法 dll 并使用它们进行调试,所以我建议您将您的类放在自己的项目中。然后,您将更容易理解这些概念:
对语法满意后,需要从项目中引用它并解析输入字符串:
class Program
{
static void Main(string[] args)
{
var g = new OData();
var l = new LanguageData(g);
var r = new Parser(l);
var p = r.Parse("((Name eq 'John' or Name eq 'Grace Paul') and (Department eq 'Finance and Accounting'))"); // here's your tree
// this is where you walk it and extract whatever data you desire
}
}
然后,您所要做的就是遍历生成的树并根据语法节点类型应用您的自定义逻辑。在this SO answer 中可以找到一个如何做到这一点的示例。
根据您的要求,您可能会发现这对于您的目的来说完全是矫枉过正,或者实际上可能会发现它为您提供的控制水平是完全正确的。