ANTLR неявное умножение

Я плохо знаком с ANTLR, и я пытаюсь подробно остановиться на примере простого представленного калькулятора здесь. Определенно, я попытался добавить некоторые простые функции, отрицательные числа и так далее, ознакомить меня с ANTLR. Однако я столкнулся с чем-то вроде проблемы, пытающейся осуществить "неявное" умножение (например, 3cos (2), грех (2) интерпретировался бы как 3*cos (2) *sin (2)).

Я нашел вопрос на Переполнении стека с тем же самым видом проблемы (здесь). Общая форма решения той проблемы похожа на то, что я нашел самостоятельно, таким образом, я не уверен, где моя проблема заключается.

Моя грамматика ниже. Без | p2 = signExpr {$value * = $p2.value;} линия (последняя линия в multiplicationExpr ), все, кажется, хорошо работает согласно моим тестам. Когда я добавляю эту линию и управляю ею через antlr , я получаю следующие ошибки:

error(211): calculator.g:24:3: [fatal] rule multiplicationExpr has non-LL(*) decision due to recursive rule invocations reachable from alts 3,4.  Resolve by left-factoring or using syntactic predicates or using backtrack=true option.
warning(200): calculator.g:24:3: Decision can match input such as "'-' FLOAT" using multiple alternatives: 3, 4
As a result, alternative(s) 4 were disabled for that input

Предоставление возможности отступление результаты в неправильных вычислениях для части из моего (обычно работающий) проверяемые выражения. Далее, предупреждение говорит об альтернативах 3 и 4 для multiplicationExpr , но у меня только есть три альтернативы в том блоке, у которого есть я перепутанный.

Кто-то был бы в состоянии указать на ошибку в моей грамматике, данной ниже?

grammar calculator;

eval    returns [double value]
        : exp = additionExpr    {$value = $exp.value;}
        ;

additionExpr    returns [double value]
        :               m1 = multiplicationExpr {$value = $m1.value;}
                ( '+'   m2 = multiplicationExpr {$value += $m2.value;}
                | '-'   m2 = multiplicationExpr {$value -= $m2.value;}
                )*
        ;

multiplicationExpr      returns [double value]
        :               p1 = signExpr   {$value = $p1.value;}
                ( '*'   p2 = signExpr   {$value *= $p2.value;}
                | '/'   p2 = signExpr   {$value /= $p2.value;}
                |       p2 = signExpr   {$value *= $p2.value;}
                )*
        ;

signExpr        returns [double value]
        :       (       '-' a = funcExpr        {$value = -1*$a.value;}
                ) | (   a = funcExpr            {$value = $a.value;}
                )
        ;

funcExpr        returns [double value]
        :       (       'cos' s = signExpr      {$value = Math.cos($s.value);}
                ) | (   'sin' s = signExpr      {$value = Math.sin($s.value);}
                ) | (   s = powExpr             {$value = s;}
                )
        ;

powExpr returns [double value]
        :               s1 = atomExpr   {$value = $s1.value;}
                ( '^'   s2 = signExpr   {$value = Math.pow($value, $s2.value);}
                )?
        ;

atomExpr        returns [double value]
        :       f = FLOAT                       {$value = Double.parseDouble($f.text);}
        |       '(' exp = additionExpr ')'      {$value = $exp.value;}
        ;

FLOAT
    :   ('0'..'9')+ ('.' ('0'..'9')*)? EXPONENT?
    |   '.' ('0'..'9')+ EXPONENT?
    ;

WS  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) {$channel=HIDDEN;}
    ;

fragment
EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
4
добавлено отредактировано
Просмотры: 1
de
I' m не v3 эксперт, но я мог заставить его работать с ((signExpr signExpr) => signExpr signExpr {$value = $p1.value * $p2.value;} в multiplicationExpr после линии : p1 =... , перед ' *' , и удаление последнего | p2... линия.
добавлено автор BernardK, источник
Право, это должно было быть ((signExpr signExpr) => p1 = signExpr p2 = signExpr {$value = $p1.value * $p2.value;} . Теперь результат результат =-0.37840124765396416 вместо 0.0, учитывая вход 3cos (2) грех (2) .
добавлено автор BernardK, источник
Я didn' t вполне понимают, как поместить это в мою грамматику в том месте (произвел тот же самый вид ошибки), но я получил вещи, работающие, поместив тот же самый вид вещи, '(signExpr funcExpr +) => p1=signExpr p2=funcExpr {$value = $p1.value * $p2.value;}' в начале multiplicationExpr . Я также изменил второй signExpr на funcExpr, так как моя грамматика начала интерпретировать некоторые вычитания как умножение (например, 3-4 как 3implicitMultplication-4 . Так или иначе, благодарит указать мне к решению!
добавлено автор Tyler, источник
Ой, мое решение didn' t обращаются с несколькими неявными продуктами и I' ve выяснил ваше решение (с тем же самым изменением funcExpr выше).. не уверенный, почему я wasn' t то, чтобы заставлять его работать в первый раз. Еще раз спасибо!
добавлено автор Tyler, источник

2 ответы

Новый год, новая версия:-)

Поскольку вы плохо знакомы с ANTLR, и теперь видели некоторые проблемы двусмысленности с v3, вы будете, конечно, ценить власть v4. Следующее решение (я плохо знаком с v4), хранение вычисления в уровне экспрессии, но показ, как простой это должно описать выражения с ANTLR4.

grammar Calculator;

progr : eval+ EOF ;

eval 
    :   mult ';' {System.out.println($eval.text + " -> " + $mult.value);}
    ;

mult    returns [double value]
    :   e1 = expr                  {$value = $e1.value;}
    |   e1 = expr e2 = mult        {$value = $e1.value * $e2.value;}
    ;

expr    returns [double value]
    :   e1 = expr '^' e2 = expr   {$value = Math.pow($e1.value, $e2.value);}
    |   '-' e1 = expr              {$value = -1 * $e1.value;}
    |   e1 = expr '*' e2 = expr    {$value = $e1.value * $e2.value;}
    |   e1 = expr '/' e2 = expr    {$value = $e1.value/$e2.value;}
    |   e1 = expr '+' e2 = expr    {$value = $e1.value + $e2.value;}
    |   e1 = expr '-' e2 = expr    {$value = $e1.value - $e2.value;}
    |   'cos' s = expr             {$value = 2; System.out.println("cos" + $s.text + "=2");}//{$value = Math.cos($s.value);}
    |   'sin' s = expr             {$value = 5; System.out.println("sin" + $s.text + "=5");}//{$value = Math.sin($s.value);}
    |   FLOAT                      {$value = Double.parseDouble($FLOAT.text); System.out.println("FLOAT=" + $FLOAT.text);}
    |   '(' FLOAT ')'              {$value = $expr.value;}//just for demo, to avoid printing FLOAT parameters
    |   '(' e1 = expr ')'          {$value = $e1.value; System.out.println("expr(" + $e1.text + ")=" + $e1.value);}
    ;

FLOAT
    :   DIGIT+ ( '.' DIGIT* )? EXPONENT?
    |   '.' DIGIT+ EXPONENT?
    ;

WS  :   [ \t\r\n] -> channel(HIDDEN)
    ;

fragment DIGIT    : [0-9] ;
fragment EXPONENT : [Ee] ( '+' | '-' )? DIGIT+ ;

Файл input.txt:

3cos(2)sin(6);
4cos(2)cos(8)sin(6);
sin(1)sin(6);
sin(1)sin(6)cos(8)10sin(8);
sin(1)5cos(6)3;
2^8;
3*-12;
55/sin(0);
55/(2 + 3);

Выполнение:

$ echo $CLASSPATH
.:/usr/local/lib/antlr-4.0b3-complete.jar
$ alias
alias antlr4='java -jar /usr/local/lib/antlr-4.0b3-complete.jar'
alias grun='java org.antlr.v4.runtime.misc.TestRig'
$ antlr4 Calculator.g4 
$ javac Calculator*.java
$ grun Calculator progr input.txt 
FLOAT=3
cos(2)=2
sin(6)=5
3cos(2)sin(6); -> 30.0
FLOAT=4
cos(2)=2
cos(8)=2
sin(6)=5
4cos(2)cos(8)sin(6); -> 80.0
sin(1)=5
sin(6)=5
sin(1)sin(6); -> 25.0
sin(1)=5
sin(6)=5
cos(8)=2
FLOAT=10
sin(8)=5
sin(1)sin(6)cos(8)10sin(8); -> 2500.0
sin(1)=5
FLOAT=5
cos(6)=2
FLOAT=3
sin(1)5cos(6)3; -> 150.0
FLOAT=2
FLOAT=8
2^8; -> 256.0
FLOAT=3
FLOAT=12
3*-12; -> -36.0
FLOAT=55
sin(0)=5
55/sin(0); -> 11.0
FLOAT=55
FLOAT=2
FLOAT=3
expr(2 + 3)=5.0
55/(2 + 3); -> 11.0

Примечания:
1) Я дал фиксированные значения потому что и грех, чтобы быть в состоянии легко проверить вычисления.
2) Управляемый с - диагностика и вы будете видеть сообщения двусмысленности:

$ grun Calculator progr -diagnostics input.txt 
FLOAT=3
line 1:6 reportAttemptingFullContext d=2, input='(2)'
line 1:6 reportAmbiguity d=2: ambigAlts={5, 6}, input='(2)'

Это вызвано тем, что я добавил линию:

    |   '(' FLOAT ')'              {$value = $expr.value;}//just for demo

напечатать приятно (стараются не печатать параметры к потому что/грех). Это неоднозначно с ' (' e1 = expr')' . Удалите его, и сообщения исчезают.
3) Чтобы видеть, как ANTLR4 размечает (-символы) и разборы (-след):

grun Calculator progr -tokens -diagnostics -trace input.txt

4) site : http://antlr4.org
5) book : http://pragprog.com/book/tpantlr2/the-definitive-antlr-4-reference
6) install 4.0b3 on OS X : http://forums.pragprog.com/forums/206/topics/11231
7) SO filter : https://stackoverflow.com/questions/tagged/antlr4
8) group : https://groups.google.com/forum/#!forum/antlr-discussion

4
добавлено
Очень интересный. Спасибо за подробный пример!
добавлено автор Tyler, источник

С решением BernardK, массажировавшим в мою предыдущую грамматику, вот новое multiplicationExpr , который получает все работающее на меня:

multiplicationExpr      returns [double value]
        :
                        p1 = signExpr   {$value = $p1.value;}
                ( (signExpr funcExpr*) => p2 = funcExpr {$value *= $p2.value;}
                | '*'   p2 = signExpr   {$value *= $p2.value;}
                | '/'   p2 = signExpr   {$value /= $p2.value;}
                )*
        ;

После немного большего количества игры вокруг, что-то близко к тому, что у меня первоначально были работы также:

multiplicationExpr      returns [double value]
        :               p1 = signExpr   {$value = $p1.value;}
                (       p2 = funcExpr   {$value *= $p2.value;}
                | '*'   p2 = signExpr   {$value *= $p2.value;}
                | '/'   p2 = signExpr   {$value /= $p2.value;}
                )*
        ;

Еще раз спасибо, Бернард.

1
добавлено
Я также спешил в своем первом комментарии. То, что я имел в виду, захватывало две последовательных функции с синтаксическим предикатом, чтобы снять неоднозначность. По крайней мере, это бежит, результат теперь-1.1352037429618924. Но это правильно? I' m все еще ищущий...
добавлено автор BernardK, источник
Поздравления! Настолько простой.
добавлено автор BernardK, источник
Ну, оказывается, что я, возможно, использовал (p2 = funcExpr {$value * = $p2.value;} все время, который является тем, с чего я начал, заменив signExpr с funcExpr . Наличие signExpr - то, что вызывало ошибки (и это имеет смысл что подписанное выражение can' t быть второй частью неявного умножения. Тогда нет никакого синтаксического предиката, требуемого в этом случае.
добавлено автор Tyler, источник