c#中antlr语法/解析器的无效强制转换问题

本文关键字:无效 问题 转换 antlr 语法 | 更新日期: 2023-09-27 17:50:59

我使用Anlr 3.5从下面包含的语法生成解析器和词法分析器。该语法将用于读取字符串,以便将它们转换为对象图以供以后求值。然而,当我试图使用unaryExpression子句时,我遇到了问题,这样做会抛出一个类强制转换异常报告:

无法将"operanexpression_return"类型的对象强制转换为"unaryExpression_return"类型。

导致这种情况的一个输入示例是
([p1 eq '"p1Value'"]) and [p2 eq '"p2Value'"] or [p3 > '"p3'"'"Val'"'"ue'"]

似乎是显式触发异常的括号(unaryExpression)。运行相同的语句减去括号似乎可以正确解析,括号内的子表达式也是如此。

我可以通过异常清楚地看到使用了错误的类型。我只是不明白为什么解析器选择它,也不明白语法需要什么纠正来避免这个错误。

值得注意的是,表达式子句的两个分支确实与每个binaryOperator分支都有多个匹配替代方案,但从我的理解来看,这应该是可以的,只会导致效率较低的贪婪解析器,而不会导致我所看到的错误。

语法使用:

grammar QueryExpressionGrammar;
options {
    language=CSharp3;
    TokenLabelType=CommonToken;
    output=AST;
    ASTLabelType=CommonTree;
}
@lexer::namespace{namespace}
@parser::namespace{namespace}
@parser::members {
public static string CleanQuotedString(string input)
{
    return input == null
        ? null
        : input.Substring(1, input.Length - 2).Replace("'"'"", "'"");
}
}
/*------------------------------------------------------------------
 * PARSER RULES
 *------------------------------------------------------------------*/
public parse returns [IQueryExpression value]
    : exp=expression EOF {$value = $exp.value;}
    ;
expression returns [IQueryExpression value]
    : lhs=operandExpression { $value = $lhs.value; } ( op=binaryOperator rhs=expression {$value = new BinaryOperationQueryExpression($lhs.value, $op.value, $rhs.value);} )*
    | lhs=unaryExpression { $value = $lhs.value; } ( op=binaryOperator rhs=expression {$value = new BinaryOperationQueryExpression($lhs.value, $op.value, $rhs.value);} )*
    ;
binaryOperator returns [BinaryOperator value]
    : BinaryOperatorAnd {$value = BinaryOperator.And;}
    | BinaryOperatorOr {$value = BinaryOperator.Or;}
    ;
unaryExpression  returns [IQueryExpression value]
    : UnaryOperatorNot sub=expression {$value = new UnaryOperationQueryExpression(UnaryOperator.Not, $sub.value);}
    | OPEN_PAREN sub=expression CLOSE_PAREN {$value = new UnaryOperationQueryExpression(UnaryOperator.Paren, $sub.value);}
    ;
operandExpression returns [IQueryExpression value]
    : OPEN_BRACKET p=PROPERTY OperandQueryExpressionOperatorEq v=QUOTED_STRING CLOSE_BRACKET {$value = new OperandQueryExpression($p.text, OperandQueryExpressionOperator.Eq, CleanQuotedString($v.text));}
    | OPEN_BRACKET p=PROPERTY OperandQueryExpressionOperatorLt v=QUOTED_STRING CLOSE_BRACKET {$value = new OperandQueryExpression($p.text, OperandQueryExpressionOperator.Lt, CleanQuotedString($v.text));}
    | OPEN_BRACKET p=PROPERTY OperandQueryExpressionOperatorLe v=QUOTED_STRING CLOSE_BRACKET {$value = new OperandQueryExpression($p.text, OperandQueryExpressionOperator.Le, CleanQuotedString($v.text));}
    | OPEN_BRACKET p=PROPERTY OperandQueryExpressionOperatorGt v=QUOTED_STRING CLOSE_BRACKET {$value = new OperandQueryExpression($p.text, OperandQueryExpressionOperator.Gt, CleanQuotedString($v.text));}
    | OPEN_BRACKET p=PROPERTY OperandQueryExpressionOperatorGe v=QUOTED_STRING CLOSE_BRACKET {$value = new OperandQueryExpression($p.text, OperandQueryExpressionOperator.Ge, CleanQuotedString($v.text));}
    | OPEN_BRACKET p=PROPERTY OperandQueryExpressionOperatorLike v=QUOTED_STRING CLOSE_BRACKET {$value = new OperandQueryExpression($p.text, OperandQueryExpressionOperator.Like, CleanQuotedString($v.text));}
    | OPEN_BRACKET p=PROPERTY OperandQueryExpressionOperatorIlike v=QUOTED_STRING CLOSE_BRACKET {$value = new OperandQueryExpression($p.text, OperandQueryExpressionOperator.Ilike, CleanQuotedString($v.text));}
    | OPEN_BRACKET p=PROPERTY NonValueBasedOperandQueryExpressionOperatorIsNull CLOSE_BRACKET {$value = new OperandQueryExpression($p.text, OperandQueryExpressionOperator.IsNull, null);}
    | OPEN_BRACKET p=PROPERTY NonValueBasedOperandQueryExpressionOperatorNotNull CLOSE_BRACKET {$value = new OperandQueryExpression($p.text, OperandQueryExpressionOperator.NotNull, null);}
    ;
/*------------------------------------------------------------------
 * LEXER RULES
 *------------------------------------------------------------------*/
UnaryOperatorNot
    : 'not'
    | '!'
    ;
BinaryOperatorAnd
    : 'and'
    | '&&'
    | '&'
    ;
BinaryOperatorOr
    : 'or'
    | '||'
    | '|'
    ;
// OperandQueryExpressionOperator that uses a comparison value
OperandQueryExpressionOperatorEq
    : 'eq'
    | '=='
    | '='
    ;
OperandQueryExpressionOperatorLt
    : 'lt'
    | '<'
    ;
OperandQueryExpressionOperatorLe
    : 'le'
    | '<='
    ;
OperandQueryExpressionOperatorGt
    : 'gt'
    | '>'
    ;
OperandQueryExpressionOperatorGe
    : 'ge'
    | '>='
    ;
OperandQueryExpressionOperatorLike
    : 'like'
    ;
OperandQueryExpressionOperatorIlike
    : 'ilike'
    ;
// OperandQueryExpressionOperator that does not use a comparison value
NonValueBasedOperandQueryExpressionOperatorIsNull
    : 'null'
    | 'isnull'
    ;
NonValueBasedOperandQueryExpressionOperatorNotNull
    : 'notnull'
    ;
OPEN_BRACKET: '[';
CLOSE_BRACKET:  ']';
OPEN_PAREN: '(';
CLOSE_PAREN: ')';
PROPERTY: LETTER (ALPHA_NUMERIC | SPECIAL)*; // property definition is in line with the definition of a property definition for c#
QUOTED_STRING: QUOTE (~QUOTE | (QUOTE QUOTE))* QUOTE; // values are characters, or if they contain quotes they are escaped by double quoting and surrounding value in quotes
fragment DIGIT: ('0'..'9');
fragment LETTER: (('a'..'z')|('A'..'Z'));   
fragment ALPHA_NUMERIC: (LETTER|DIGIT);
fragment SPECIAL: ('_'|'-');    
fragment QUOTE: ''u0022';
WHITESPACE: (' ' | ''t' | ''n' | ''r'){ Skip(); }; // valid whitespace characters

解析器中抛出异常的代码片段在私有QueryExpressionGrammarParser中。Expression_return expression() method

...
switch (alt3)
{
case 1:
    DebugEnterAlt(1);
    // QueryExpressionGrammar.g:37:4: lhs= operandExpression (op= binaryOperator rhs= expression )*
    {
    root_0 = (CommonTree)adaptor.Nil();
    DebugLocation(37, 7);
    PushFollow(Follow._operandExpression_in_expression111);
    lhs=operandExpression();
    PopFollow();
    adaptor.AddChild(root_0, lhs.Tree);
    DebugLocation(37, 26);

    /// v v v Exception throwing on line below v v v
     retval.value = (lhs!=null?((QueryExpressionGrammarParser.unaryExpression_return)lhs).value:default(IQueryExpression)); 

    DebugLocation(37, 51);
    // QueryExpressionGrammar.g:37:51: (op= binaryOperator rhs= expression )*
    try { DebugEnterSubRule(1);
...

c#中antlr语法/解析器的无效强制转换问题

这就是我们在评论中找到的解决方案。修改这个:

expression returns [IQueryExpression value]
    : lhs=operandExpression { $value = $lhs.value; } ( op=binaryOperator rhs=expression {$value = new BinaryOperationQueryExpression($lhs.value, $op.value, $rhs.value);} )*
    | ulhs=unaryExpression { $value = $ulhs.value; } ( op=binaryOperator rhs=expression {$value = new BinaryOperationQueryExpression($ulhs.value, $op.value, $rhs.value);} )*
    ;