ANTLR - 语法和树语法之间的令牌枚举不匹配

发布于 2024-12-20 07:35:25 字数 4427 浏览 2 评论 0原文

背景

我正在尝试使用 AntlrWorks 为布尔方程编写一个简单的语法,用于测试指定元素是否存在(或缺乏)的值集。 我创建了一个组合的词法分析器/解析器语法,可以生成所需的 AST。 我还编写了一个相应的树语法,似乎可以工作(通过AntlrWorks的调试功能)。


问题

但是,当我尝试在测试程序中将两者连接在一起(即同一程序中的 lex、parse 和 tree parse)时,我收到如下错误...

node from line 1:5 required (. ..)+ 循环与输入 'and' 处的任何内容均不匹配

以及

第 1 行之后的 节点:8 不匹配的树节点:UP 期望

作为健全性测试,我有测试程序输出的结果来自生成的 AST 的 toStringTree() 和来自生成的 TreeNodeStreamtoTokenTypeString()

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


示例

  • 示例输入:“true 和 false”

  • 来自提供的解析器的 toStringTree() 输出树:(和 true false)

  • toTokenTypeString 的输出来自 TreeNodeStream 的 ()包含上述 AST:19 2 22 20 3 8

该令牌流应该是 AND'真' '假'换行符 但 TreeParser 将其视为 CLOSEPAREN或 '假'OPENPAREN (基于查看节点标记类型输出并根据树语法中定义的枚举进行检查)并抛出错误

1:5 required (...)+循环不匹配任何内容at input 'and'


底线

为什么我的树解析器没有设置为正确识别我的 AST?

下面是我的源代码。我很感激任何关于我一定犯过的愚蠢错误的反馈:)

Lexer/Parser Grammar

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

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;}
;

Test Program

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());

    }   
}

BackGround

I am trying to write a simple grammar, using AntlrWorks, for boolean equations that test sets of values for the existence (or lack there of) of specified elements.
I have created a combined lexer/parser grammar that produces the desired ASTs.
I have also written a cooresponding tree grammar, that seems to work (passes AntlrWorks' debug functions).


Problem

However, When I try to wire the two together in a test program (that is lex, parse, and tree parse in the same program), I get errors like...

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

and

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

As a sanity test, I had the test program output the results of toStringTree() from the generated AST and toTokenTypeString() from the resulting TreeNodeStream.

What I found was that the enumerated token type values of the TreeNodeStream do not match the enumerated token type values in the autogenerated Tree Grammar code.


EXAMPLE

  • sample input: "true and false"

  • Output of toStringTree() from the Parser supplied tree: (and true false)

  • Output of toTokenTypeString() from TreeNodeStream conatining the above AST: 19 2 22 20 3 8

That token stream should be AND <DOWN> 'true' 'false' <UP> NEWLINE
But the TreeParser sees it as CLOSEPAREN <DOWN> OR 'false' <UP> OPENPAREN (based off of looking at the node token type output and checking it against the enumeration defined in the tree grammar) and throws the error

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


Bottom Line

Why isn't my tree parser set up to properly identify my ASTs?

Below is my source. I appreciate any feedback on the foolish mistakes I must have made :)

Lexer/Parser Grammar

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

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;}
;

Test Program

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());

    }   
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

宣告ˉ结束 2024-12-27 07:35:25

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

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

即:我看到预期的输出:

  • 树没问题((和真假));
  • CommonTreeNodeStream 包含适当的标记(或者更好:树);
  • 并且正确的值 false 正在被打印,解析器或树遍历器没有任何错误。

一些提示:

  • 'true''false' 创建标记(即 TRUE='true'; ...);
  • 不要在树语法中使用文字(不是 'true',而是 TRUE);
  • DIGIT 设为 fragment 规则,这样它就永远不会成为自己的标记,而仅在 INT (或其他词法分析器规则)内使用。只需将关键字 fragment 放在其前面即可;
  • 默认情况下,.*.+ 都是非贪婪的,因此您可以删除 options gredy=false;} :

If I use your combined grammar and tree grammar to create lexer, parser and tree-walker classes, and run the following class:

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());
  }
}

the following is printed to the console:

(and true false) 

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

result=false

I.e.: I see the expected output:

  • the tree is okay ((and true false));
  • CommonTreeNodeStream contains the proper tokens (or better: trees);
  • and the correct value, false, is being printed without any errors from either the parser or tree walker.

A couple of tips:

  • create tokens for both 'true' and 'false' (i.e. TRUE='true'; ...);
  • don't use literals inside your tree grammar (not 'true', but TRUE);
  • make DIGIT a fragment rule, that way it will never become a token of its own, but only used inside INT (or other lexer rules). Simply place the keyword fragment in front of it;
  • both .* and .+ are ungreedy by default, so you can remove the options greedy=false;} :.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文