これが今日の私の 1000 番目の質問のようです :) 文法の完成に近づいています。前置演算子と中置演算子が同じ記号を共有している場合に問題が発生します。MathML として知られるマークアップ言語を解析しています...
grammar MathMLOperators;
options
{
output = AST;
backtrack = true;
memoize = true;
}
tokens
{
DOCUMENT; // The root of the parsed document.
GROUP;
OP; // any operator
PREFIX_OP; // a prefix operator.
INFIX_OP; // an infix operator.
POSTFIX_OP; // a postfix operator.
NON_INFIX_OP; // a non-infix operator
}
// Start rule.
public document : math+ -> ^(DOCUMENT math+);
inFixTag : TAG_START_OPEN MO TAG_CLOSE ('-' | '+' | '=') TAG_END_OPEN MO TAG_CLOSE -> ^(INFIX_OP);
preFixTag : TAG_START_OPEN MO TAG_CLOSE ('+' | '-') TAG_END_OPEN MO TAG_CLOSE -> ^(PREFIX_OP);
// Use semantic predicate to only allow postfix expressions when at the end of an mrow.
postFixTag : TAG_START_OPEN MO TAG_CLOSE ('!' | '^') TAG_END_OPEN MO {input.LT(1).getType() == TAG_CLOSE && input.LT(2).getType() == TAG_END_OPEN && input.LT(3).getType() == MROW && input.LT(4).getType() == TAG_CLOSE}? TAG_CLOSE -> ^(POSTFIX_OP);
nonInfixTag : TAG_START_OPEN MO TAG_CLOSE ('!' | '^') TAG_END_OPEN MO TAG_CLOSE {$expressionList::count++;} -> ^(OP);
opTag: TAG_START_OPEN MO TAG_CLOSE ('-' | '+' | '^' |'=') TAG_END_OPEN MO TAG_CLOSE -> ^(NON_INFIX_OP);
//Expressions
infixExpression: grouping (inFixTag^ grouping)*;
grouping : nestedExpression+ -> ^(GROUP nestedExpression+);
prefixExpression : /* check that it's the first in the mrow*/ {$expressionList::count == 0}? (preFixTag^ (primaryExpression | nonInfixTag)) {$expressionList::count++;};
postfixExpression : (primaryExpression | prefixExpression| nonInfixTag) (postFixTag^)? ;
expressionList scope {int count} @init{$expressionList::count = 0;} : (infixExpression | opTag)+;
nestedExpression : postfixExpression;
primaryExpression : mrow | mn;
math : TAG_START_OPEN root=MATH TAG_CLOSE expressionList TAG_END_OPEN MATH TAG_CLOSE -> ^($root expressionList);
mrow : TAG_START_OPEN root=MROW TAG_CLOSE expressionList? TAG_END_OPEN MROW TAG_CLOSE -> ^($root expressionList?);
mn: TAG_START_OPEN root=MN TAG_CLOSE INT TAG_END_OPEN MN TAG_CLOSE -> ^($root INT);
MATH : 'math'; // root tag
MROW : 'mrow'; // row
MO : 'mo'; // operator
MN : 'mn'; // number
TAG_START_OPEN : '<';
TAG_END_OPEN : '</' ;
TAG_CLOSE : '>';
TAG_EMPTY_CLOSE : '/>';
INT : '0'..'9'+;
WS : (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN;};
これはうまくいきます...
<math>
<mrow>
<mo>-</mo>
<mn>7</mn>
<mo>=</mo>
<mn>8</mn>
</mrow>
</math>
しかし、これは失敗します...
<math>
<mrow>
<mo>-</mo>
<mn>7</mn>
<mo>-</mo>
<mn>8</mn>
</mrow>
</math>
最初の「-」は「プレフィックス」、2 番目は「インフィックス」にする必要があります。デバッガーからは、ルールがループしていて、一致しない場合でもgrouping
親ルールに戻らないようです。infixExpression
どこかで EBNF 演算子が間違っていることは確かですが、どれが間違っているのかわかりません。C などの言語で見られる標準的な式の入れ子パターンに従おうとしましたが、これは解析する言語としては珍しいものです。