使用 System.Linq.Expressions 命名空间评估 ANTLR 识别的逻辑表达式

问题描述 投票:0回答:1

我定义了一个语法,我必须在 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 值?

我还处于设计阶段,我已经完成了语法定义并为其生成监听器基类。

c# linq antlr antlr4 boolean-expression
1个回答
0
投票

我会简单地扩展 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
© www.soinside.com 2019 - 2024. All rights reserved.