我正在尝试使用ANTLR4为一种相当简单的语言制作语法。它应该处理一些与戏剧有关的文本。只有3条规则。
1 -任何以制表符(t
)开头的文本都应该直接打印出来。
It was a rather warm
Summer day.
2 -如果文本不以制表符开始,它很可能包含字符名称。例如:
Captain Go forth, my minions!
将字符名称和他们所说的文本分开获取将是完美的。
3 -还有一些命令,也以制表符开头,后跟一个关键字和一些参数,类似于:
lights ON
curtain OPEN
这是我的语法:
grammar Theater;
module: statement+ EOF;
statement: function | print | print_with_name;
function: 't' command NL;
command: lights | curtain;
lights: 'lights' WS ('ON' | 'OFF');
curtain: 'curtain' WS ('OPEN' | 'CLOSE');
print: PRINT;
PRINT: 't' .*? NL NL;
print_with_name: PRINT_WITH_NAME;
PRINT_WITH_NAME: ~[ trn] .*? NL NL;
NL: 'rn' | 'r' | 'n';
WS: [ t]+?;
我在下面的测试文件上运行这个命令:
It was a rather warm
Summer day.
Captain Go forth, my minions!
lights ON
curtain OPEN
这些是我得到的记号:
[@0,0:22='tIt was a rather warmrn',<PRINT>,1:0]
[@1,23:36='tSummer day.rn',<PRINT>,2:0]
[@2,37:67='Captain Go forth, my minions!rn',<PRINT_WITH_NAME>,3:0]
[@3,68:79='tlights ONrn',<PRINT>,4:0]
[@4,80:94='tcurtain OPENrn',<PRINT>,5:0]
[@5,95:94='<EOF>',<EOF>,6:0]
print
和print with name
均按预期工作。另一方面,命令被视为print
。我猜,这是因为那些是词法分析器规则,而命令是解析器规则。是否有任何方法可以使它工作而不将所有命令转换为词法分析器规则?我试着写一些类似于"将所有文本视为打印,除非它以其中一个关键字"开头。但我找不到任何有效的方法。我只从鹿茸开始,所以我一定遗漏了什么。
我不指望你帮我写语法。只要提到一个我应该使用的功能就可以了。
词法分析器模式在这里很有帮助,这是一种将词法分析器推到正确方向的方法(使其对上下文敏感)。
要使用词法分析器模式,必须将词法分析器和解析器语法划分到单独的文件中。TheaterLexer.g4
:
lexer grammar TheaterLexer;
Name : ~[ t]+ -> mode(DialogMode);
K_Lights : 'tlights' -> mode(CommandMode);
K_Curtain : 'tcurtain' -> mode(CommandMode);
Tab : 't' -> skip, mode(TabMode);
mode DialogMode;
DialogText : ~[rn]+;
DialogNewLine : [rn]+ -> skip, mode(DEFAULT_MODE);
mode CommandMode;
CommandText : ~[rn]+;
CommandNewLine : [rn]+ -> skip, mode(DEFAULT_MODE);
mode TabMode;
LiteralText : ~[rn]+;
LiteralNewLine : [rn]+ -> skip, mode(DEFAULT_MODE);
解析器部分(放在TheaterParser.g4
中):
parser grammar TheaterParser;
options { tokenVocab=TheaterLexer; }
parse
: file EOF
;
file
: atom*
;
atom
: literal
| dialog
| command
;
literal
: LiteralText+
;
dialog
: Name DialogText+
;
command
: K_Lights CommandText+
| K_Curtain CommandText+
;
如果现在生成词法分析器和解析器类并运行以下Java代码:
String source =
"tIt was a rather warmn" +
"tSummer day.n" +
"Captain Go forth, my minions!n" +
"tlights ONn" +
"tcurtain OPEN";
TheaterLexer lexer = new TheaterLexer(CharStreams.fromString(source));
TheaterParser parser = new TheaterParser(new CommonTokenStream(lexer));
ParseTree root = parser.parse();
System.out.println(root.toStringTree(parser));
将打印以下内容到您的控制台:
(parse
(file
(atom
(literal It was a rather warm Summer day.))
(atom
(dialog Captain Go forth, my minions!))
(atom
(command tlights ON))
(atom
(command tcurtain OPEN))) <EOF>)
(为了便于阅读,增加了缩进)
请注意,您只能使用单一模式,但我假设您希望在不同的模式中以不同的方式处理令牌。如果不是这样,您可以这样做:
lexer grammar TheaterLexer;
Name : ~[ t]+ -> mode(Step2Mode);
K_Lights : 'tlights' -> mode(Step2Mode);
K_Curtain : 'tcurtain' -> mode(Step2Mode);
Tab : 't' -> skip, mode(Step2Mode);
mode Step2Mode;
Text : ~[rn]+;
NewLine : [rn]+ -> skip, mode(DEFAULT_MODE);
,并相应地更改解析器规则。