我认为您的课程没有正确反映表达式的结构。让我使用EBNF写下此类表达式的可能语法(不要求完整性):
Expression = Term { ("AND" | "OR") Term }.
Term = "(" Expression ")" | Comparison.
Comparison = name ("=" | "!=" | "<" ...) Value.
Value = stringConstant | number.
我们看到的一件重要的事情是 aTerm
可以是 anExpression
或 a Comparison
。AND
这意味着我们必须能够将一个或另一个插入到 or 的两侧OR
。示例:Name = "Maulik" AND (Id = 2 OR Id = 3)
。左边AND
是比较,右边是表达式。
因此,我们必须使这两个事物的赋值兼容,例如,通过从同一个基类派生它们。
public abstract class Term
{
}
public Expression : Term
{
public Term FirstTerm { get; set; }
public List<(BooleanOperator operator, Term term)> SucceedingTerms { get; } = new();
}
public Comparison : Term
{
public string Name { get; set; }
public ComparisonOperator Operator { get; set; }
public object Value { get; set; }
}
另一个重要的事实是语法是递归的。即,一个表达式包含术语,而术语可以包含另一个表达式。这对于能够表示具有多级嵌套的括号表达式是必要的。
语法的这种递归性质有两个后果:
- 类结构必须允许递归结构。这是因为
Expression
该类有一个包含Term
s 的列表,而这些列表又可以是Expression
s。
- 后面显示的解析器必须是递归的。它包含间接递归调用(
ParseExpression
调用ParseTerm
和ParseTerm
调用ParseExpression
)。
请注意,我使用ValueTulpes作为由运算符和术语组成的列表元素。
现在,到转换本身。你需要一个编译器。任何一个
由于语法简单,您可能敢于自己编写编译器。
将任务分为词法分析(由词法分析器完成)和语法分析(由解析器完成)。除了分析语法外,解析器还创建所需的数据结构作为输出。词法分析器只返回一个符号流。例如
enum Symbol { EndOfInput, LeftPar, RightPar, AndOperator, OrOperator, Identifier, Number,
StringLiteral, ...}
private string _input;
private int _currentPos;
private string _identifier;
private string _stringValue;
private decimal _number;
private Symbol _symbol;
/// Does the lexical analysis.
private void GetSymbol()
{
// Get next `_symbol` (and associated variables) from `_input` at `_currentPos` and
// update `_currentPos`.
}
使用此基本基础架构,您可以创建解析器。解析器由密切反映语法结构的方法组成(如上面的 EBNF 中给出的)。
// Expression = Term { ("AND" | "OR") Term }.
void Expression ParseExpression()
{
var expression = new Expression();
expression.FirstTerm = ParseTerm();
while (_symbol == Symbol.AndOperator || _symbol == Symbol.OrOperator) {
var op = _symbol == Symbol.AndOperator
? BooleanOperator.And
: BooleanOperator.Or;
GetSymbol();
term = ParseTerm();
expression.SucceedingTerms.Add((op, term));
}
return expression;
}
// Term = "(" Expression ")" | Comparison.
void Term ParseTerm()
{
Term term = null;
if (Symbol == Symbol.LeftPar) {
GetSymbol();
term = ParseExpression();
if (Symbol == Symbol.RightPar) {
GetSymbol();
} else {
Error("\")\" expected.");
}
} else {
term = ParseComparison();
}
return term;
}
这并不完整,但你明白了。由于此解析器的结构取决于语法产生 (EBNF),因此在确定确切的语法之前不要从它开始。
请注意在下一个符号给出的语法中始终具有可确定的路径。首先,我错了。我的任期由Term = "(" Expression | Comparison ")" | Comparison.
. 这里的问题是一个表达式以一个术语开头,而一个术语可以是一个比较。比较从名称开始。因此,两者都Expression
可以Comparison
以名称开头。当解析 Expression | Comparison
并且下一个符号是名称时,我们无法决定是否必须解析表达式或比较。
更新的语法是Term = "(" Expression ")" | Comparison.
. 现在,我们知道如果下一个符号是左括号,我们必须解析表达式,否则解析比较。