ANTLR - Несоответствие перечисления токенов между грамматикой и грамматикой дерева - PullRequest
4 голосов
/ 09 декабря 2011

BackGround

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


Задача

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

node from line 1:5 required (...)+ loop did not match anything at input 'and'

и

node from after line 1:8 mismatched tree node: UP expecting <DOWN>

В качестве проверки работоспособности у меня была тестовая программа, выводившая результаты toStringTree() из сгенерированного AST и toTokenTypeString() из полученного TreeNodeStream.

Я обнаружил, что перечисленные значения типа токена TreeNodeStream не соответствуют перечисленным значениям типа токена в автоматически сгенерированном коде древовидной грамматики.


Пример

  • пример ввода: "true and false"

  • Вывод toStringTree () из предоставленного парсером дерева: (and true false)

  • Вывод toTokenTypeString() из TreeNodeStream, содержащий вышеуказанное AST: 19 2 22 20 3 8

Этот поток токенов должен быть AND <DOWN> 'true' 'false' <UP> NEWLINE Но TreeParser видит его как CLOSEPAREN <DOWN> OR 'false' <UP> OPENPAREN (основанный на просмотре вывода типа токена узла и проверке его на соответствие перечислению, определенному в грамматике дерева), и выдает ошибку

1:5 required (...)+ loop did not match anything at input 'and'


Итог

Почему мой анализатор дерева не настроен для правильной идентификации моих AST?

Ниже мой источник. Я ценю любые отзывы о глупых ошибках, которые я должен был сделать:)

грамматика лексера / синтаксического анализатора

grammar INTc;

options {
   output=AST;
   ASTLabelType=CommonTree;
}

tokens {
   OR='or';
   AND='and';
   NOT='not';
   ALLIN='+';
   PARTIN='^';
   NOTIN='!';
   SET;
   OPENPAREN='(';
   CLOSEPAREN=')';
   OPENSET='{';
   CLOSESET='}';
}
@header {
package INTc;
}

@lexer::header {
package INTc;
}

@members {
}

/**Begin Parser Rules*/
prog:   stat+ ;

stat:   expr
    |   NEWLINE
    ;

expr
:  orExpr
;

orExpr returns [boolean value]
    :   a=andExpr(OR^ b=andExpr)*
    ;

andExpr returns [boolean value]
    :   a=notExpr (AND^ b=notExpr)*
    ; 

notExpr returns [boolean value]
    :   a=atom
    | '!' a=atom -> ^(NOT atom)
    ;

atom returns [boolean value]
    :   ALLIN  OPENSET ((INT)(','INT)*) CLOSESET   -> ^(ALLIN ^(SET INT+))
    |   PARTIN  OPENSET ((INT)(','INT)*) CLOSESET  -> ^(PARTIN ^(SET INT+))
    |   NOTIN OPENSET ((INT)(','INT)*) CLOSESET   -> ^(NOTIN  ^(SET INT+))
    |   TIMERANGE
    |   OPENPAREN! e=expr CLOSEPAREN!
    |   'true'
    |   'false'
    ;

/**Begin Lexer Rules*/
ID  :   ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')* ;
DIGIT   :   ('0'..'9');
INT :   DIGIT+ ;
NEWLINE :   '\r'? '\n' ;
WS  :   ( ' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};
COMMENT
    :   '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    |   '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
    ;

Грамматика деревьев

tree grammar INTcWalker;

options {
  tokenVocab=INTc;
  ASTLabelType=CommonTree;
}

@header {
  package INTc;
  import java.util.ArrayList;
  import java.util.Arrays;
}

@members {
  ArrayList<String> intSet;
  boolean isFit = false;

  public boolean getResult() {
     return isFit;   
  }
  public void setINTSet(ArrayList newSet) {
     intSet = newSet;
     isFit = false;
  }
  public ArrayList getINTSET(){return intSet;}
}

prog
:     stat+
;
stat
:     expr  {
                                     isFit = $expr.value;
                                     //System.out.println(isFit);
    }
|    NEWLINE {}
;
expr returns [boolean value]
: ^(OR a=expr b=expr){}
| ^(AND a=expr b=expr){}
| ^(NOT a=expr){}
| ^(ALLIN ^(SET INT+)){}
| ^(PARTIN ^(SET INT+)){}
| ^(NOTIN ^(SET INT+)){}
| 'true'        {$value = true;}
| 'false'       {$value = false;}
;

Тестовая программа

public class setTest {

    public static void main(String args[]) throws Exception {
        INTcLexer lex = new INTcLexer(new ANTLRFileStream("input.txt"));
        CommonTokenStream tokens = new CommonTokenStream(lex);

        INTcParser parser = new INTcParser(tokens);
        INTcParser.prog_return r = parser.prog();
        CommonTree t  = (CommonTree)r.getTree();
        CommonTreeNodeStream nodes = new CommonTreeNodeStream(t);
        INTcWalker evaluator = new INTcWalker(nodes);

        System.out.println(t.toStringTree());

        System.out.println(nodes.toTokenTypeString());
        nodes.reset();

        try {
                evaluator.prog();
        } catch (RecognitionException e) {
                e.printStackTrace();
        }   

        System.out.println(evaluator.getResult());

    }   
}

1 Ответ

3 голосов
/ 09 декабря 2011

Если я использую вашу объединенную грамматику и древовидную грамматику для создания классов лексера, анализатора и обходчика дерева и запускаю следующий класс:

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;

public class Main {
  public static void main(String args[]) throws Exception {
    INTcLexer lex = new INTcLexer(new ANTLRStringStream("true and false\n"));
    CommonTokenStream tokens = new CommonTokenStream(lex);
    INTcParser parser = new INTcParser(tokens);

    CommonTree t  = (CommonTree)parser.prog().getTree();
    CommonTreeNodeStream nodes = new CommonTreeNodeStream(t);
    INTcWalker evaluator = new INTcWalker(nodes);

    System.out.println(t.toStringTree());

    CommonTree tr;
    while(true) {
      Token token = ((CommonTree)nodes.nextElement()).getToken();
      if(token.getType() == INTcParser.EOF) break;
      System.out.printf("%-10s '%s'\n", INTcParser.tokenNames[token.getType()], token.getText());
    }

    System.out.println("\nresult=" + evaluator.getResult());
  }
}

на консоль выводится следующее:

(and true false) 

AND        'and'
<DOWN>     'DOWN'
'true'     'true'
'false'    'false'
<UP>       'UP'
NEWLINE    '
'

result=false

Т.е.: я вижу ожидаемый результат:

  • с деревом все в порядке ((and true false));
  • CommonTreeNodeStream содержит правильные токены (или лучше: деревья));
  • и правильное значение, false, печатается без ошибок из анализатора или обходчика дерева.

Пара советов:

  • создавать токены для 'true' и 'false' (то есть TRUE='true'; ...);
  • не используйте литералы внутри грамматики дерева (не 'true', ноTRUE);
  • сделать DIGIT правилом fragment, таким образом, оно никогда не станет собственным токеном, а будет использоваться только внутри INT (или других правил лексера).Просто поместите ключевое слово fragment перед ним;
  • и .*, и .+ по умолчанию не являются жадными, поэтому вы можете удалить options greedy=false;} :.
...