3.4 构建一个语言类应用程序
我们继续完成能够处理数组初始化语句的示例程序,下一个目标是能够翻译初始化语句,而不仅仅是能够识别它们。例如,我们想要将Java中,类似{ 99, 3, 451 }的short数组翻译成"\u0063\u0003\u01c3"。注意,其中十进制数字99的十六进制表示是63。
为了完成这项工作,程序必须能够从语法分析树中提取数据。最简单的方案是使用ANTLR内置的语法分析树遍历器进行深度优先遍历,然后在它触发的一系列回调函数中进行适当的操作。正如我们之前看到的那样,ANTLR能够自动生成一个监听器接口和一个默认的实现类。这样的监听器非常类似于图形界面程序控件上的回调函数(例如,当一个按钮被按下时,它会通知我们)或者XML解析器中的SAX事件。
我们如果想要通过编写程序来操纵输入的数据的话,只需要继承ArrayInitBaseListener类,然后覆盖其中必要的方法即可。我们的基本思想是,在遍历器进行语法分析树的遍历时,令每个监听器方法翻译输入数据的一部分并将结果打印出来。
监听器机制的优雅之处在于,我们不需要自己编写任何遍历语法分析树的代码。事实上,我们甚至都不知道ANTLR运行库是怎么遍历语法分析树、怎么调用我们的方法的。我们只知道,在语法规则对应的语句的开始和结束位置处,我们的监听器方法可以得到通知。在7.2节我们会看到,这种机制使得我们不需要了解太多ANTLR的知识——我们回到了自己熟悉的领域,即写普通的代码而不是处理语言识别问题。
一个进行翻译工作的项目意味着要处理这样的问题:如何将输入的词法符号或者词组翻译成输出字符串。为了达到这个目标,最好先从手工翻译一些有代表性的样例入手,想办法提取出通用的转换逻辑。在下例中,转换过程是非常直截了当的。
用自然语言解释,翻译过程就是一系列“X映射为Y”的过程:
1)将{翻译为"。
2)将}翻译为"。
3)将每个整数翻译为四位的十六进制形式,然后加前缀\u。
为此,我们需要编写方法,在遇到对应的输入词法符号或者词组的时候,打印出转换后的字符串。内置的语法分析树遍历器会在各种词组的开始和结束位置触发监听器的回调函数。下面是遵循我们的翻译规则的一个监听器的实现类。
一切顺利。无须深入理解语法的细节,我们就完成了我们的第一个翻译器。我们所做的一切不过是实现了几个方法,在这些方法中打印出对输入文本的适当的翻译结果。另外,我们可以通过给遍历器传递一个不同的监听器以实现完全不同的输出。监听器有效地将语言类应用程序和语法进行了解耦,从而使得同一个语法能够被不同的程序复用。
下一章,我们将快速学习ANTLR语法的编写方法,以及让ANTLR语法如此强大和易用的关键特性。