ANTLR - 语法和树语法之间的令牌枚举不匹配
背景
我正在尝试使用 AntlrWorks 为布尔方程编写一个简单的语法,用于测试指定元素是否存在(或缺乏)的值集。 我创建了一个组合的词法分析器/解析器语法,可以生成所需的 AST。 我还编写了一个相应的树语法,似乎可以工作(通过AntlrWorks的调试功能)。
问题
但是,当我尝试在测试程序中将两者连接在一起(即同一程序中的 lex、parse 和 tree parse)时,我收到如下错误...
node from line 1:5 required (. ..)+ 循环与输入 'and' 处的任何内容均不匹配
以及
第 1 行之后的 节点:8 不匹配的树节点:UP 期望
作为健全性测试,我有测试程序输出的结果来自生成的 AST 的 toStringTree()
和来自生成的 TreeNodeStream
的 toTokenTypeString()
。
我发现 TreeNodeStream 的枚举令牌类型值与自动生成的树语法代码中的枚举令牌类型值不匹配。
示例
示例输入:
“true 和 false”
来自提供的解析器的 toStringTree() 输出树:
(和 true false)
toTokenTypeString 的输出来自 TreeNodeStream 的 ()
包含上述 AST:19 2 22 20 3 8
该令牌流应该是 AND
但 TreeParser 将其视为 CLOSEPAREN
(基于查看节点标记类型输出并根据树语法中定义的枚举进行检查)并抛出错误
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果我使用组合语法和树语法来创建词法分析器、解析器和树遍历器类,并运行以下类:
以下内容将打印到控制台:
即:我看到预期的输出:
(和真假)
);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:
the following is printed to the console:
I.e.: I see the expected output:
(and true false)
);CommonTreeNodeStream
contains the proper tokens (or better: trees);false
, is being printed without any errors from either the parser or tree walker.A couple of tips:
'true'
and'false'
(i.e.TRUE='true';
...);'true'
, butTRUE
);DIGIT
afragment
rule, that way it will never become a token of its own, but only used insideINT
(or other lexer rules). Simply place the keywordfragment
in front of it;.*
and.+
are ungreedy by default, so you can remove theoptions greedy=false;} :
.