背景
我正在尝试使用AntlrWorks编写一个简单的语法,用于测试指定元素存在(或缺少)的值集的布尔方程.
我创建了一个组合的词法分析器/解析器语法,可以产生所需的AST.
我还编写了一个相似的树语法,似乎可行(通过AntlrWorks的调试功能).
问题
但是,当我尝试在一个测试程序(即同一程序中的lex,parse和tree parse)中将两者连接在一起时,我得到的错误就像…
第1行:第5行所需的节点(…)循环与输入’和’处的任何内容都不匹配
和
第1:8行不匹配的树节点之后的节点:UP期望< DOWN>
作为一个健全性测试,我让测试程序从生成的AST输出toStringTree()的结果,并从结果的TreeNodeStream输出toTokenTypeString().
我发现TreeNodeStream的枚举标记类型值与自动生成的Tree Grammar代码中的枚举标记类型值不匹配.
例
>样本输入:“真与假”
>从解析器提供的树输出toStringTree():(和true false)
>从TreeNodeStream输出toTokenTypeString(),包含上述AST:19 2 22 20 3 8
该令牌流应该是AND< DOWN> ‘true”false’< UP>新队
但TreeParser将其视为CLOSEPAREN< DOWN>或’假’< UP> OPENPAREN(基于查看节点令牌类型输出并根据树语法中定义的枚举检查它)并抛出错误
1:5必需(…)循环与输入’和’处的任何内容都不匹配
底线
为什么我的树解析器没有设置为正确识别我的AST?
以下是我的来源.我感谢任何有关我必须犯下的愚蠢错误的反馈:)
Lexer / Parser语法
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 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成为一个片段规则,这样它永远不会成为它自己的标记,但只在INT(或其他词法规则)中使用.只需将关键字片段放在它前面;
>两者.*和.默认情况下是不合适的,所以你可以删除选项greedy = false;} :.