处理同一文件中的不同格式

目前我们看到的输入文件都只包含一种语言,但在实际应用中我们会遇到有些包含多种语言的常用文件格式。例如,Java的文档注释,XML文件等。这些环绕着模板表达式的文本需要不同的处理方式,它们被称为孤岛语言。

ANTLR有提供一个称之为“词法模型”的词法分析器特性,它让我们可以很容易地处理包含混合格式的文件。基本思路是:当词法分析器看到特殊的哨兵字符序列时,让它在模式之间来回切换。

XML是一个很好例子,它通常会在同一个文件中包含不同的词法结构。一个XML语法分析器会把除标签和实体引用(例如♥)之外的任何东西当作文本块。当词法分析器看到<时,它切换到“inside”模式;当它看到>/>时,就切换回默认模式。以下语法展示了该特性是如何工作的:

lexer grammar XMLLexer;

// Default "mode": Everything OUTSIDE of a tag
OPEN        :   '<'                 -> pushMode(INSIDE) ;
COMMENT     :   '<!--' .*? '-->'    -> skip ;
EntityRef   :   '&' [a-z]+ ';' ;
TEXT        :   ~('<'|'&')+ ;    // match any 16 bit char minus < and &

// ----------------- Everything INSIDE of a tag ---------------------
mode INSIDE;

CLOSE       :   '>'                 -> popMode ;    // back to default mode
SLASH_CLOSE :   '/>'                -> popMode ;
EQUALS      :   '=' ;
STRING      :   '"' .*? '"' ;
SlashName   :   '/' Name ;
Name        :   ALPHA (ALPHA|DIGIT)* ;
S           :   [ \t\r\n]           -> skip ;

fragment
ALPHA       :   [a-zA-Z] ;

fragment
DIGIT       :   [0-9] ;

把上述语法保存为XMLLexer.g文件,然后使用包含以下内容的t.xml文件作为输入来测试它:

<tools>
  <tool name="ANTLR">A parser generator</tool>
</tools>

以下是构建和运行测试的命令:

antlr XMLLexer.g
compile *.java
grun XML tokens -tokens t.xml

这里是输出的内容:

[@0,0:0='<',<1>,1:0]
[@1,1:5='tools',<10>,1:1]
[@2,6:6='>',<5>,1:6]
[@3,7:10='\r\n  ',<4>,1:7]
[@4,11:11='<',<1>,2:2]
[@5,12:15='tool',<10>,2:3]
[@6,17:20='name',<10>,2:8]
[@7,21:21='=',<7>,2:12]
[@8,22:28='"ANTLR"',<8>,2:13]
[@9,29:29='>',<5>,2:20]
[@10,30:47='A parser generator',<4>,2:21]
[@11,48:48='<',<1>,2:39]
[@12,49:53='/tool',<9>,2:40]
[@13,54:54='>',<5>,2:45]
[@14,55:56='\r\n',<4>,2:46]
[@15,57:57='<',<1>,3:0]
[@16,58:63='/tools',<9>,3:1]
[@17,64:64='>',<5>,3:7]
[@18,65:66='\r\n',<4>,3:8]
[@19,67:66='<EOF>',<-1>,4:0]

上面输出的每一行代表一个记号,包含记号索引、开始和结束字符、记号文本、记号类型,最后的行和字符位置则告诉我们词法分析器如何标记化输入。

在命令行中,XML tokens序列处通常是一个语法名字后面跟着开始规则,但在这里,我们使用语法名字后面跟着特殊的规则名字tokens来告诉TestRig应该运行词法分析器而不是语法分析器。接着使用选项-tokens打印出匹配的记号列表。


书籍推荐