ANTLR - 语法和树语法之间的标记枚举不匹配



BackGround

我正在尝试使用 AntlrWorks 为布尔方程编写一个简单的语法,用于测试是否存在(或缺乏(指定元素的值集。我创建了一个组合的词法分析器/解析器语法来生成所需的 AST。我还编写了一个cooresponse树语法,它似乎有效(传递AntlrWorks的调试函数(。


问题

但是,当我尝试在测试程序中将两者连接在一起(即同一程序中的 lex、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>

作为健全性测试,我让测试程序从生成的 AST 输出toStringTree()结果,从生成的TreeNodeStream输出toTokenTypeString()结果。

我发现TreeNodeStream的枚举令牌类型值与自动生成的树语法代码中的枚举令牌类型值不匹配。


  • 示例输入:"true and false"

  • 解析器提供的树中输出 toStringTree((:(and true false)

  • 符合上述 AST 的 TreeNodeStream 的toTokenTypeString()输出: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());
    }   
}

如果我使用您的语法和树语法组合来创建词法分析器、解析器和树行者类,并运行以下类:

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 falsen"));
    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;} :

最新更新