我定义了一个语法,我必须在 C# 中解析它并在字典列表上评估结果的“约束”。语法主要是布尔代数。
您可以在这里查看:
grammar Constraint;
expr
: implyExpr
;
implyExpr
: orExpr
| leftOp=orExpr IMPLY rightOp=orExpr
;
orExpr
: andExpr
| leftOp=andExpr OR rightOp=andExpr
;
andExpr
: primaryExpr
| leftOp=primaryExpr AND rightOp=primaryExpr
;
primaryExpr
: trueExpr
| falseExpr
| parenExpr
| notParenExpr
| statement
;
trueExpr
: TRUE
;
falseExpr
: FALSE
;
parenExpr
: LPAREN expr RPAREN
;
notParenExpr
: NOT LPAREN expr RPAREN
;
statement
: eqValStatement
| notEqValStatement
| inListStatement
| notInListStatement
;
eqValStatement
: key=STR EQ value=val
;
notEqValStatement
: key=STR NEQ value=val
;
inListStatement
: key=STR EQ list=lst
;
notInListStatement
: key=STR NEQ list=lst
;
lst
: LBRACK values+=val (COMMA values+=val)* RBRACK
;
val
: strVal
| nullVal
;
strVal
: value=STR
;
nullVal
: NULL
;
// Lexer
IMPLY: '->';
OR: '|';
AND: '&';
TRUE: 'true';
FALSE: 'false';
LPAREN: '(';
RPAREN: ')';
NOT: '!';
EQ: '=';
NEQ: '!=';
LBRACK: '[';
RBRACK: ']';
COMMA: ',';
NULL: 'null';
STR: [a-zA-Z0-9-_]+;
WS: [ \t\n\r]+ -> skip;
我的问题是,我可以使用这个命名空间来实现我的目标,还是应该使用不同的委托,这将采用 2 个委托作为参数,并根据实现的行为返回一个 bool 值?
我还处于设计阶段,我已经完成了语法定义并为其生成监听器基类。
我会简单地扩展 ANTLR 生成的访问者(在生成解析器类时需要添加
-visitor
参数)。可能看起来像这样:
public class ConstraintEvalVisitor : ConstraintBaseVisitor<object?>
{
// eval
// : expr EOF
// ;
public override object? VisitEval(ConstraintParser.EvalContext context)
=> this.Visit(context.expr());
public override object? VisitExpr(ConstraintParser.ExprContext context)
=> this.Visit(context.implyExpr());
public override object? VisitImplyExpr(ConstraintParser.ImplyExprContext context)
{
if (context.IMPLY() == null)
{
return this.Visit(context.orExpr().Single());
}
var lhs = this.Visit(context.leftOp) as bool?;
var rhs = this.Visit(context.rightOp) as bool?;
return lhs!.Value ? rhs! : true;
}
public override object? VisitOrExpr(ConstraintParser.OrExprContext context)
{
if (context.OR() == null)
{
return this.Visit(context.andExpr().Single());
}
var lhs = this.Visit(context.leftOp) as bool?;
var rhs = this.Visit(context.rightOp) as bool?;
return lhs!.Value || rhs!.Value;
}
public override object? VisitAndExpr(ConstraintParser.AndExprContext context)
{
if (context.AND() == null)
{
return this.Visit(context.primaryExpr().Single());
}
var lhs = this.Visit(context.leftOp) as bool?;
var rhs = this.Visit(context.rightOp) as bool?;
return lhs!.Value && rhs!.Value;
}
public override object? VisitPrimaryExpr(ConstraintParser.PrimaryExprContext context)
{
if (context.trueExpr() != null)
return true;
if (context.falseExpr() != null)
return false;
if (context.parenExpr() != null)
return this.Visit(context.parenExpr().expr());
if (context.notParenExpr() != null)
return this.Visit(context.notParenExpr());
return this.Visit(context.statement());
}
public override object? VisitNotParenExpr(ConstraintParser.NotParenExprContext context)
=> this.Visit(context.expr()) as bool?;
public override object? VisitStatement(ConstraintParser.StatementContext context)
{
if (context.eqValStatement() != null)
return this.Visit(context.eqValStatement());
if (context.notEqValStatement() != null)
return this.Visit(context.notEqValStatement());
if (context.inListStatement() != null)
return this.Visit(context.inListStatement());
return this.Visit(context.notInListStatement());
}
public override object? VisitEqValStatement(ConstraintParser.EqValStatementContext context)
{
var lhs = context.STR().GetText();
var rhs = this.Visit(context.val()) as string;
return lhs.Equals(rhs);
}
public override object? VisitNotEqValStatement(ConstraintParser.NotEqValStatementContext context)
{
var lhs = context.STR().GetText();
var rhs = this.Visit(context.val()) as string;
return !lhs.Equals(rhs);
}
public override object? VisitInListStatement(ConstraintParser.InListStatementContext context)
{
var lhs = context.STR().GetText();
var rhs = this.Visit(context.lst()) as List<object?>;
return rhs!.Contains(lhs);
}
public override object? VisitNotInListStatement(ConstraintParser.NotInListStatementContext context)
{
var lhs = context.STR().GetText();
var rhs = this.Visit(context.lst()) as List<object?>;
return !rhs!.Contains(lhs);
}
public override object? VisitVal(ConstraintParser.ValContext context)
=> context.strVal() == null ? null : context.strVal().STR().GetText();
public override object? VisitLst(ConstraintParser.LstContext context)
=> context._values.Select(this.Visit).ToList();
}
可以这样测试:
Evaluate("true -> true");
Evaluate("true -> false");
Evaluate("false -> true");
Evaluate("false -> false");
Evaluate("false | false");
Evaluate("false | true");
Evaluate("true & false");
Evaluate("true & true");
Evaluate("(true) & true");
Evaluate("A = A");
Evaluate("A = B");
Evaluate("A != A");
Evaluate("A != B");
Evaluate("B = [A, B]");
Evaluate("A = [B, C, D]");
static void Evaluate(string expression)
{
var lexer = new ConstraintLexer(CharStreams.fromString(expression));
var parser = new ConstraintParser(new CommonTokenStream(lexer));
var answer = new ConstraintEvalVisitor().Visit(parser.eval());
Console.WriteLine($"{expression,-20}{answer}");
}
将打印:
true -> true True
true -> false False
false -> true True
false -> false True
false | false False
false | true True
true & false False
true & true True
(true) & true True
A = A True
A = B False
A != A False
A != B True
B = [A, B] True
A = [B, C, D] False