【发布时间】:2011-06-08 04:01:40
【问题描述】:
基本上,我需要提前知道某个标记是否存在,但不匹配它(即,另一个解析器规则仍然可以匹配它)。
问题的确切细节是“END-ALL”子句。 该语言具有“IF”(由“END-IF”封闭)、“FOR”(由“END-FOR”封闭)等结构。
但可以选择使用“END-ALL”全局关闭所有此类开环(从而无需实际的“END-IF”或“END-FOR”子句)。
无论如何我可以正确地实现这个吗?
【问题讨论】:
标签: antlr
基本上,我需要提前知道某个标记是否存在,但不匹配它(即,另一个解析器规则仍然可以匹配它)。
问题的确切细节是“END-ALL”子句。 该语言具有“IF”(由“END-IF”封闭)、“FOR”(由“END-FOR”封闭)等结构。
但可以选择使用“END-ALL”全局关闭所有此类开环(从而无需实际的“END-IF”或“END-FOR”子句)。
无论如何我可以正确地实现这个吗?
【问题讨论】:
标签: antlr
您可以通过在您的if(和for-)语句中创建一个布尔标志来跟踪是否需要使用ENDALL,或者前瞻是否足够。这个布尔标志被传递给匹配 if 代码块结尾的解析器规则。
一个小演示:
grammar T;
options {
output=AST;
}
tokens {
BLOCK;
ASSIGN;
}
@parser::members {
private boolean flag = true;
}
parse
: block EOF -> block
;
block
: stat* -> ^(BLOCK stat*)
;
stat
: PRINT expression -> ^(PRINT expression)
| assignment
| ifStat
;
assignment
: ID '=' expression -> ^(ASSIGN ID expression)
;
ifStat
@init{
boolean consumeEndAll = false;
if(flag) {
consumeEndAll = true;
flag = false;
}
}
@after {
if(consumeEndAll) {
flag = true;
}
}
: IF expression DO block end[consumeEndAll] -> ^(IF expression block)
;
expression
: NUMBER
| TRUE
| FALSE
| ID
;
end [boolean consumeEndAll]
: END
| EOF
| {consumeEndAll}?=> ENDALL
| {input.LT(1).getType() == ENDALL}?=> { /* consume no token */ }
;
PRINT : 'print';
ENDALL : 'endall';
END : 'end';
IF : 'if';
DO : 'do';
TRUE : 'true';
FALSE : 'false';
NUMBER : '0'..'9'+ ('.' '0'..'9'+)?;
ID : ('a'..'z' | 'A'..'Z')+;
SPACE : (' ' | '\t' | '\r' | '\n') {skip();};
end 规则 ({ ... }?=>) 中的谓词导致规则要么使用 ENDALL,要么只向前看是否存在此类令牌,但不使用它。
关于谓词的更多信息:What is a 'semantic predicate' in ANTLR?
上述语法生成的解析器将为脚本 1 和 2 生成相同的 AST:
if 1 do
print a
if 2 do
print b
print c
if 3 do
end
end
end
print d
if 1 do
print a
if 2 do
print b
print c
if 3 do
endall
print d
即以下AST:
(使用graphviz-dev.appspot.com生成的图像)
您可以使用以下 Java 类来测试这一切:
import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;
public class Main {
public static void main(String[] args) throws Exception {
String source =
"if 1 do \n" +
" print a \n" +
" if 2 do \n" +
" print b \n" +
" print c \n" +
" if 3 do \n" +
"endall \n" +
"print d ";
System.out.println(source + "\n------------------\n");
TLexer lexer = new TLexer(new ANTLRStringStream(source));
TParser parser = new TParser(new CommonTokenStream(lexer));
CommonTree tree = (CommonTree)parser.parse().getTree();
DOTTreeGenerator gen = new DOTTreeGenerator();
StringTemplate st = gen.toDOT(tree);
System.out.println(st);
}
}
【讨论】: