扫描器(Scanner,词法分析器)是编译器/解释器前端的一个组件,用来执行读取源程序,将程序分成Token等语法动作。解析器(语法分析器)每次需要从源程序获取Token时就会调用扫描器。本章你将会为Pascal Token设计和开发一个扫描器。

==>> 本章中文版源代码下载:svn co http://wci.googlecode.com/svn/branches/ch3/ 源代码使用了UTF-8编码,下载到本地请修改!

目标和方法

前面章节中,你已在前端实现一个语言无关的Scanner框架类和一个初级版本与Pascal有关的框架子类PascalScanner。本章的目标是完成Pascal扫描器的设计与开发并测试它。扫描器将能够:

  • 从源程序中抽取Pascal单词,数字,字符串以及特殊符号Token(比如+,:= 等)。
  • 判断一个单词Token是一个标识符(identifier,一般标识变量名称,程序名称,过程/函数名称等)还是一个Pascal关键字(reserved word)。
  • 计算数字token的值并判断它是一个整数还是实数(real,包含小数点)。
  • 处理语法错误。

本章的方法是开发一系列可被PascalScanner创建的Pascal token子类。你将创建一个框架类Token的子类PascalToken并接着开发PascalToken的子类用来表示Pascal单词,数字,特殊符号token等。你会定义Pascal Token类型并实现上一章的TokenType接口。

图3-1 概括了我们的扫描器设计

图3-1:Pascal相关的Scanner和Token类

(基于Java)编写编译器和解释器-第3章:扫描-第一部分(连载)灰色部分表示你已在frontend包中实现的语言无关的框架类。现在你将完成frontend.pascal包中的Pascal子类。你也将创建frontend.pascal.tokens包用来容纳PascalToken子类。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

设计笔记

定义将单个的Pascal Token类型子类分开是策略设计模式的另一个例子。这儿的策略是从源文件中创建各种token类型的算法。在每个子类封装自己策略(通过extract()方法实现)使得代码更加模块化,也使得扫描器能够更容易的选择合适的token类型去创建。所有token子类将继承自PascalToken类,而PascalToken继承自语言无关的Token类。

图3-1 也演示了上一章开始处提到的设计准则:永远基于可运行代码进行构建。此时你正在给早些时候开发的框架添加子类。

就如第一章提到的,扫描器也称词法分析器,它的扫描操作也称词法分析。Token同样有另一个名字:Lexeme(词单位)

程序-3:Pascal分词器

从frontend.pascal包中,上一章开发的解析器子类PascalParserTD开始,扩展它的parser方法以便练习和测试整个Pascal扫描器。尽管你还没有编写它需要的各种token子类,parser方法还是展示了怎样扫描各种Pascal Token并处理错误。见清单3-1。

清单3-1:PascalParserTD类中的Parse和getErrorCount方法

extends Parser {
   2:  
new PascalErrorHandler();
   4:  
public PascalParserTD(Scanner scanner) {
super(scanner);
   7:     }
   8:  
/**
     * Pascal的解析过程,产生Pascal相关的iCode和symbol table
     */
throws Exception {
  13:         Token token;
long startTime = System.currentTimeMillis();
  15:  
try {
instanceof EofToken)) {
  18:                 TokenType tokenType = token.getType();
if (tokenType != PascalTokenType.ERROR) {
new Message(MessageType.TOKEN,
new Object[] { token.getLineNumber(),
  22:                                     token.getPosition(), token.getType(),
  23:                                     token.getText(), token.getValue() }));
else {
// 留意当token有问题是,它的值表示其错误编码
  26:                     errorHandler.flag(token,
this);
  28:                 }
  29:             }
// 发送编译摘要信息
float elapsedTime = (System.currentTimeMillis() - startTime) / 1000f;
new Number[] {
  33:                     token.getLineNumber(), getErrorCount(), elapsedTime }));
catch (IOException e) {
this);
  36:         }
  37:     }
  38:  
int getErrorCount() {
return errorHandler.getErrorCount();
  41:     }
  42: }

相关文章:

  • 2021-12-07
  • 2021-07-28
  • 2022-01-13
  • 2021-11-20
  • 2021-07-05
  • 2021-10-16
  • 2021-09-08
  • 2021-08-05
猜你喜欢
  • 2022-02-21
  • 2021-07-25
  • 2021-08-13
  • 2022-01-30
  • 2021-12-10
  • 2022-01-14
  • 2021-11-27
相关资源
相似解决方案