Anlr 3.5 を使用して、以下に含まれる文法からパーサーとレクサーを生成しています。この文法は、後で評価するために文字列をオブジェクト グラフに変換するために文字列を読み取るために使用されます。ただし、 unaryExpression句を使用しようとすると問題が発生し、クラス キャスト例外レポートがスローされます。
タイプ「operandExpression_return」のオブジェクトをタイプ「unaryExpression_return」にキャストできません。
これを引き起こす入力の一例は次のとおりです。
([p1 eq \"p1Value\"]) and [p2 eq \"p2Value\"] or [p3 > \"p3\"\"Val\"\"ue\"]
例外を明示的にトリガーするのは括弧 (unaryExpression) のようです。括弧を除いた同じステートメントを実行すると、括弧内のサブ式と同様に、適切に解析されるように見えます。
間違ったタイプが使用されている例外を介してはっきりとわかります。パーサーがそれを選択する理由や、このエラーを回避するために文法にどのような修正が必要かについては、私は知りません。
式句の 2 つの分岐にはそれぞれ、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() メソッド内のパーサー内で例外をスローするコードのスニペット
...
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);
...