第一部分

标识符是怎样被定义的?

你再也不能简单的认为标识符就是变量了。一个标识符的符号表项必须指明它是怎么定义的,比如作为常量,还是类型,或者记录域或是变量等等。一个标识符也有一个类型,此类型必须被它对应的符号表项指示出来。因此,需要给包intermediate中的SymTabEntry接口加入新的setter和getter方法。如下面的清单9-10:

public TypeSpec getTypeSpec();

这些新的方法直接实现在包intermediate.symtabimpl中的类SymTabEntryImpl类中,这个类必须有两个新域:

private TypeSpec typeSpec; 

清单9-11 展示了新的Definition接口

public String getText();
}

清单9-12 展示了包intermediate.symtabimpl中的枚举类DefinitionImpl,它实现了Definition接口。它的所有枚举值呈现了标识符被定义的各种方式。

implements Definition
   2: {
),
),
),
),
/*函数*/,
/*未定义*/;
   9:  
private String text;
  11:  
  12:     DefinitionImpl()
  13:     {
this.toString().toLowerCase();
  15:     }
  16:  
  17:     DefinitionImpl(String text)
  18:     {
this.text = text;
  20:     }
  21:  
public String getText()
  23:     {
return text;
  25:     }
  26: }

预定义类型和常量

清单9-13 展示了类Predefined。它的静态域呈现了Pascal的预定义整数,实数(浮点数),布尔和字符类型附加一个未定义类型,以及预定义的标识符 integer、real、boolean、char、false和true。

公共的静态方法intialize初始化这些预定义的类型和标识符,后面你将会看到解析器在开始分析一个程序在嵌套层0(全局)调用这个方法。

私有方法initializeTypes()创建每个预定义类型的规范和标识符。它用了类型工厂创建相关TypeSpec对象。它为类型标识符创建符号表项并设置定义和类型规范。

私有方法initializeConstants()为预定义的布尔常量false和true创建对应的符号表项并设置相应的定义和类型规范,以及CONSTANT_VALUE属性(false用0,true用1表示)。这个方法设置boolean类型规范的ENUMERATION_CONSTANTS属性为这两个符号表项。

解析Pascal申明

你得移步到前端(刚才一直在中间层),准备分析Pascal申明了。图9-5 展示了申明解析的UML类图。

(基于Java)编写编译器和解释器-第9章:解析声明-第二部分(连载)

用来解析Pasal块(block)的类BlockParser,是PascalParserTD的一个子类。如同语法图9-1所示,一个块包含申明接着是符合语句,所以BlockParser依赖同样是PascalParserTD的另外一个子类DeclarationParser。类DeclarationsParser拥有子类ConstantsDefinitionParser、TypeDefinitionParser和VariableDeclarationParser并依赖它们。

另一个PascalParserTD的子类是TypeSpecificationParser,它拥有子类SimpleTypeParser、SubrangeTypeParser、EnumerationTypeParser、ArrayTypeParser以及RecordTypeParser。这些子类与与语法图中元素一一对应。每个子类有一个parse()方法返回一个TypeSpec对象。 TypeSpecificationParser依赖SimpleTypeParser、ArrayTypeParser和RecordTypeParser,而SimpleTypeParser依赖SubrangeTypeParser和EnumerationTypeParser。

TypeDefinitionsParser和VariableDeclarationsParser都依赖TypeSpecificationParser,因为类型定义和变量申明都含有类型说明信息。

 


申明的加入使得我们需要更新PascalParserTD的parse()方法。详见清单9-14

//待解析程序的名字
private SymTabEntry routineId; 
/**
 * Pascal的解析过程,产生Pascal相关的iCode和symbol table
 */
throws Exception {
   7:  
long startTime = System.currentTimeMillis();
   9:     iCode = ICodeFactory.createICode();
//初始化预定义
  11:     Predefined.initialize(symTabStack);
//先放一个占位名字,后面会更新
.toLowerCase());
  14:     routineId.setDefinition(DefinitionImpl.PROGRAM);
  15:     symTabStack.setProgramId(routineId);
  16:  
//每个程序都有自己的符号表,在层次1
  18:     routineId.setAttribute(SymTabKeyImpl.ROUTINE_SYMTAB, symTabStack.push());
  19:     routineId.setAttribute(SymTabKeyImpl.ROUTINE_ICODE, iCode);
this);
try {
  22:         Token token = nextToken();
// 一个程序由块开始
  24:         ICodeNode rootNode = blockParser.parse(token, routineId);
  25:         iCode.setRoot(rootNode);
  26:         symTabStack.pop();
  27:         
  28:         token = currentToken();
//一个程序以符号'.'结束
if (token.getType() != PascalTokenType.DOT) {
this);
  32:         }
// 没意义,不过我还是放在这,尊重作者。
// 发送编译摘要信息
float elapsedTime = (System.currentTimeMillis() - startTime) / 1000f;
new Number[] {
  37:                 token.getLineNumber(), getErrorCount(), elapsedTime }));
catch (IOException e) {
this);
  40:     }
  41: }

新的域routineId(第2行)指向被解析程序的符号表所在项。它是主程序的名称标识符(比如java里面的main入口函数),目前是个虚拟假名,后面将是更新成实际的程序标识符,即过程或函数的名称标识符。

parse()方法调用Predefined.initialize()初始化预定义的类型和常量。它为程序名称标识符(现在是"DummyProgramName")创建一个符号表项。所有这些发生在嵌套0层级。而实际上,此方法将程序标识符放到了层级0的符号表。(后面你将会看到程序或函数的名称标识符总是定义在比程式创建内部标识符更高一层的作用域。换句话说,如果一个过程或函数的名称标识符定义在第n层,则它内部的标识符就会定义在n+1层。记录类型的名称标识符也是同样道理

第18,19两行语句为标识符对应的符号表项设置ROUTINE_SYMTAB符号表属性。它是一个新的符号表,通过symTabStack.push()推入到堆栈的嵌套层级1.第19行语句设置ROUTINE_ICODE属性为中间码。每个程序,过程,函数都会有它们独立的符号表和中间码。

因为你现在是以程式名称对应的符号表项存储每个程式的中间码引用,你需要在框架类Parser中移除所有指向私有域iCode的引用(意思是说通过标识符的表项就能找到中间码,直接引用就没必要了)。

新增放在包frontend.pascal.parsers中的Pascal解析器子类BlockParser负责解析程序块和后面的过程和函数块。清单9-15 展示了它的parse()方法,它调用declarationsParser.parse()解析一个块的申明,调用statementParser.parse()解析块中的符合语句。

public ICodeNode parse(Token token, SymTabEntry routineId)
throws Exception
   3: {
this);
this);
   6:  
   7:     declarationsParser.parse(token);
   8:  
   9:     token = synchronize(StatementParser.STMT_START_SET);
  10:     TokenType tokenType = token.getType();
  11:     ICodeNode rootNode = null;
  12:  
// 判断是否是一个复合语句,否则报错并继续解析
if (tokenType == BEGIN) {
  15:         rootNode = statementParser.parse(token);
  16:     }
else {
this);
if (StatementParser.STMT_START_SET.contains(tokenType)) {
  20:             rootNode = ICodeFactory.createICodeNode(COMPOUND);
  21:             statementParser.parseList(token, rootNode, END, MISSING_END);
  22:         }
  23:     }
  24:  
return rootNode;
  26: }
  27: }

清单9-16 展示了Pascal解析器子类DeclarationsParser的parse()方法。方法一次搜寻关键字CONST,TYPE和VAR。如果找到,则分别调用constantDefinitionsParser.parse(),typeDefinitionsParser.parse()以及variableDeclarationsParser.parse()。它设置variableDeclarationsParser()的definition属性为VARIABLE。

void parse(Token token)
throws Exception
   3:     {
   4:         token = synchronize(DECLARATION_START_SET);
   5:  
//常量申明?
if (token.getType() == CONST) {
   8:             token = nextToken(); 
   9:  
  10:             ConstantDefinitionsParser constantDefinitionsParser =
this);
  12:             constantDefinitionsParser.parse(token);
  13:         }
  14:  
  15:         token = synchronize(TYPE_START_SET);
//类型申明?
if (token.getType() == TYPE) {
  18:             token = nextToken(); 
  19:  
  20:             TypeDefinitionsParser typeDefinitionsParser =
this);
  22:             typeDefinitionsParser.parse(token);
  23:         }
  24:  
  25:         token = synchronize(VAR_START_SET);
//变量申明?
if (token.getType() == VAR) {
  28:             token = nextToken(); 
  29:  
  30:             VariableDeclarationsParser variableDeclarationsParser =
this);
  32:             variableDeclarationsParser.setDefinition(VARIABLE);
  33:             variableDeclarationsParser.parse(token);
  34:         }
  35:  
  36:         token = synchronize(ROUTINE_START_SET);
//??todo 过程和函数
  38:     }

解析常量定义

申明解析器子类ConstantDefinitionsParser的parse()方法解析一串以分号隔开的常量定义。参见清单9-17,它调用symTab.lookupLocal()查找本地符号表以防同一作用域下标识符被重复定义。方法将每个标识符(常量)放入本地符号表并调用表项的appendLineNumber()记录当前源行位置。你会看到每个新的标识符被定义时(常量,类型,枚举或变量)都会调用appendLineNumber()。

清单9-17:类ConstantDefinitionsParser的parse()方法

final EnumSet<PascalTokenType> IDENTIFIER_SET =
   2:      DeclarationsParser.TYPE_START_SET.clone();
static {
   4:      IDENTIFIER_SET.add(IDENTIFIER);
   5:  }
   6:  
// 开始一个常量的集合
final EnumSet<PascalTokenType> CONSTANT_START_SET =
   9:      EnumSet.of(IDENTIFIER, INTEGER, REAL, PLUS, MINUS, STRING, SEMICOLON);
  10:  
// 等号的同步集合
final EnumSet<PascalTokenType> EQUALS_SET =
  13:      CONSTANT_START_SET.clone();
static {
  15:      EQUALS_SET.add(EQUALS);
  16:      EQUALS_SET.add(SEMICOLON);
  17:  }
  18:  
// 开始又一段申明的开始集合
final EnumSet<PascalTokenType> NEXT_START_SET =
  21:      DeclarationsParser.TYPE_START_SET.clone();
static {
  23:      NEXT_START_SET.add(SEMICOLON);
  24:      NEXT_START_SET.add(IDENTIFIER);
  25:  }
void parse(Token token)
throws Exception
  28:  {
  29:      token = synchronize(IDENTIFIER_SET);
//CONST定义为 CONST x=1; y='abcd';所以首先是一个id,然后是等号,再是一个标识符或常量
while (token.getType() == IDENTIFIER) {
  32:          String name = token.getText().toLowerCase();
  33:          SymTabEntry constantId = symTabStack.lookupLocal(name);
//没有定义则加入定义,否则报告重复定义
if (constantId == null) {
  36:              constantId = symTabStack.enterLocal(name);
  37:              constantId.appendLineNumber(token.getLineNumber());
  38:          }
else {
this);
  41:              constantId = null;
  42:          }
  43:  
  44:          token = nextToken(); 
//同步到等号
  46:          token = synchronize(EQUALS_SET);
if (token.getType() == EQUALS) {
  48:              token = nextToken(); 
  49:          }
else {
this);
  52:          }
  53:  
//解析等号后面的常量值
  55:          Token constantToken = token;
  56:          Object value = parseConstant(token);
  57:  
// 设置标识符的定义为'常量'且存入常量值
if (constantId != null) {
  60:              constantId.setDefinition(CONSTANT);
  61:              constantId.setAttribute(CONSTANT_VALUE, value);
  62:  
  63:              TypeSpec constantType =
  64:                  constantToken.getType() == IDENTIFIER
  65:                      ? getConstantType(constantToken)
  66:                      : getConstantType(value);
  67:              constantId.setTypeSpec(constantType);
  68:          }
  69:  
  70:          token = currentToken();
  71:          TokenType tokenType = token.getType();
  72:  
//搜寻每个常量结束后的分号并吞噬掉
if (tokenType == SEMICOLON) {
while (token.getType() == SEMICOLON) {
  76:                  token = nextToken();
  77:              }
  78:          }
  79:  
//开始另一个常量
if (NEXT_START_SET.contains(tokenType)) {
this);
  83:          }
  84:  
  85:          token = synchronize(IDENTIFIER_SET);
  86:      }
  87:  }

parse()方法设置表项的定义为DefinitionImpl.CONSTANT,并将CONSTANT_VALUE属性设置为常量的值。它调用getConstantType()设置表项的类型说明。

 

设计笔记

方法parse()直到它成功通过解析到的常量值得到其类型,才将对应的标识符表项更新,因为如果在解析值过程中有错误,我们可以将错误的定义标示出来,比如 pi = pi.

parseConstant()方法解析并返回常量的值,值可以是数字,字串或一个前面被当作常量定义过的标识符。参见清单9-18,它调用方法parseIdentifierConstant()处理后一种情况(标识符赋值定义),也包含对枚举常量的处理。

清单9-18:类ConstantDefinitionsParser的parseConstant() 方法。

protected Object parseConstant(Token token)
throws Exception
   3:   {
   4:       TokenType sign = null;
   5:  
//同步到可以当作常量头的token比如a=+5; b=-a都可以的
   7:       token = synchronize(CONSTANT_START_SET);
   8:       TokenType tokenType = token.getType();
   9:  
// 是否有符号
if ((tokenType == PLUS) || (tokenType == MINUS)) {
  12:           sign = tokenType;
  13:           token = nextToken(); 
  14:       }
  15:  
switch ((PascalTokenType) token.getType()) {
  17:  
case IDENTIFIER: {
return parseIdentifierConstant(token, sign);
  20:           }
//数字和字符串直接算值
case INTEGER: {
  23:               Integer value = (Integer) token.getValue();
  24:               nextToken(); 
return sign == MINUS ? -value : value;
  26:           }
  27:  
case REAL: {
  29:               Float value = (Float) token.getValue();
  30:               nextToken(); 
return sign == MINUS ? -value : value;
  32:           }
  33:  
case STRING: {
if (sign != null) {
this);
  37:               }
  38:  
  39:               nextToken(); 
return (String) token.getValue();
  41:           }
  42:  
default: {
this);
return null;
  46:           }
  47:       }
  48:   }

清单9-19: 类ConstantDefinitionsParser的parseIdentifierConstant() 方法

protected Object parseIdentifierConstant(Token token, TokenType sign)
throws Exception
   3: {
   4:     String name = token.getText().toLowerCase();
   5:     SymTabEntry id = symTabStack.lookup(name);
   6:  
   7:     nextToken();  
//未定义的常量
if (id == null) {
this);
return null;
  12:     }
  13:  
  14:     Definition definition = id.getDefinition();
  15:  
if (definition == CONSTANT) {
  17:         Object value = id.getAttribute(CONSTANT_VALUE);
  18:         id.appendLineNumber(token.getLineNumber());
  19:  
instanceof Integer) {
return sign == MINUS ? -((Integer) value) : value;
  22:         }
instanceof Float) {
return sign == MINUS ? -((Float) value) : value;
  25:         }
instanceof String) {
if (sign != null) {
this);
  29:             }
  30:  
return value;
  32:         }
else {
return null;
  35:         }
  36:     }
if (definition == ENUMERATION_CONSTANT) {
  38:         Object value = id.getAttribute(CONSTANT_VALUE);
  39:         id.appendLineNumber(token.getLineNumber());
  40:  
if (sign != null) {
this);
  43:         }
  44:  
return value;
  46:     }
//没有定义,只有标识
this);
return null;
  50:     }
//其它情况?比如变量有VARIABLE定义,但是不是常量
this);
return null;
  54:     }
  55: }

清单9-20 展示了两个版本的getConstantType()。一个版本是以一个常量值为参数,返回预定义的整数,实数,字符类型说明或调用TypeFactory.createStringType()返回一个字符串类型说明。另一个版本以类型标识符为参数,并在符号表中搜索此符号。如果找到的标识符是一个常量或枚举常量,方法返回对应符号表项的类型说明。(这个代码太简单,请参考源代码,这里不贴出来)。

解析类型定义和类型说明

清单9-21 展示了申明解析器子类TypeDefinitionsParser的parse()方法

// 同步到可以开始一个类型标识符
final EnumSet<PascalTokenType> IDENTIFIER_SET =
   3:        DeclarationsParser.VAR_START_SET.clone();
static {
   5:        IDENTIFIER_SET.add(IDENTIFIER);
   6:    }
   7:  
// 每个类型标识符后的等号同步
final EnumSet<PascalTokenType> EQUALS_SET =
  10:        ConstantDefinitionsParser.CONSTANT_START_SET.clone();
static {
  12:        EQUALS_SET.add(EQUALS);
  13:        EQUALS_SET.add(SEMICOLON);
  14:    }
  15:  
// 每个定义或声明之后的同步即可,目前只有分号
final EnumSet<PascalTokenType> FOLLOW_SET =
  18:        EnumSet.of(SEMICOLON);
  19:  
//下一个类型或声明的同步集合
final EnumSet<PascalTokenType> NEXT_START_SET =
  22:        DeclarationsParser.VAR_START_SET.clone();
static {
  24:        NEXT_START_SET.add(SEMICOLON);
  25:        NEXT_START_SET.add(IDENTIFIER);
  26:    }
  27:  
/**
    * 解析类型定义
    * @param token 初始token
    * @throws Exception
    */
void parse(Token token)
throws Exception
  35:    {
  36:        token = synchronize(IDENTIFIER_SET);
  37:  
//解析以分号隔开的类型定义
while (token.getType() == IDENTIFIER) {
  40:            String name = token.getText().toLowerCase();
  41:            SymTabEntry typeId = symTabStack.lookupLocal(name);
//放入标识符,但定义位置(等号后面解析后才知道)
if (typeId == null) {
  44:                typeId = symTabStack.enterLocal(name);
  45:                typeId.appendLineNumber(token.getLineNumber());
  46:            }
else {
this);
  49:                typeId = null;
  50:            }
  51:  
  52:            token = nextToken();  
  53:  
//同步到等号处
  55:            token = synchronize(EQUALS_SET);
if (token.getType() == EQUALS) {
  57:                token = nextToken();  
  58:            }
else {
this);
  61:            }
  62:  
// 等号后面都是类型说明,包含简单类型,数组类型和记录类型
  64:            TypeSpecificationParser typeSpecificationParser =
this);
  66:            TypeSpec type = typeSpecificationParser.parse(token);
  67:  
//标识符被定义成类型(目前有三种定义,常量,类型和变量)
if (typeId != null) {
  70:                typeId.setDefinition(TYPE);
  71:            }
  72:  
//互相引用,类型和类型标识符
if ((type != null) && (typeId != null)) {
if (type.getIdentifier() == null) {
  76:                    type.setIdentifier(typeId);
  77:                }
  78:                typeId.setTypeSpec(type);
  79:            }
else {
  81:                token = synchronize(FOLLOW_SET);
  82:            }
  83:  
  84:            token = currentToken();
  85:            TokenType tokenType = token.getType();
  86:  
// 消耗一个或多个分割用的分号;
if (tokenType == SEMICOLON) {
while (token.getType() == SEMICOLON) {
  90:                    token = nextToken(); 
  91:                }
if (NEXT_START_SET.contains(tokenType)) {
//无分号,报错
this);
  95:            }
//又一条类型定义
  97:            token = synchronize(IDENTIFIER_SET);
  98:        }
  99:    }

parse()方法解析以分号隔开的类型定义串。它首先搜寻本地符号表保证同一作用域没有重复定义。它调用 typeSpecificationParser.parse()解析类型说明,之后设置标识符对应表项的定义种类为DefinitionImpl.TYPE。方法交叉链接类型标识符的SymTabEntry和TypeSpec对象。

设计笔记

将所有Pascal类型说明的解析工作交由子类TypeSpecificationParser来处理,这与你将所有Pascal语句的解析交给StatementParser是一个道理

清单9-22 展示了Pascal解析器子类 TypeSpecificationParse的parse()方法

   1:  
//可开始三种类型标准:简单、数组或记录
final EnumSet<PascalTokenType> TYPE_START_SET =
   4:         SimpleTypeParser.SIMPLE_TYPE_START_SET.clone();
static {
   6:         TYPE_START_SET.add(PascalTokenType.ARRAY);
   7:         TYPE_START_SET.add(PascalTokenType.RECORD);
   8:         TYPE_START_SET.add(SEMICOLON);
   9:     }
  10:  
/**
     * 解析一个类型说明
     * @param token 当前token
     * @return 解析到的类型说明
     * @throws Exception
     */
public TypeSpec parse(Token token)
throws Exception
  19:     {
  20:         token = synchronize(TYPE_START_SET);
  21:  
switch ((PascalTokenType) token.getType()) {
//数组必须是以 'array' 开头,array是关键字
case ARRAY: {
this);
return arrayTypeParser.parse(token);
  27:             }
//记录类型必须'record'开头,record是关键字
case RECORD: {
this);
return recordTypeParser.parse(token);
  32:             }
  33:             
default: {
this);
return simpleTypeParser.parse(token);
  37:             }
  38:         }
  39:     }

这里的parse()方法返回一个TypeSpec对象。如果当前的token是关键字array,方法返回arrayTypeParser.parse()得到的结果;如果是关键字record,返回recordTypeParser.parse()的值;如果都不是,则返回 simpleTypeParser.parse()得到的简单对象说明。

简单类型

清单9-23 展示了类型说明解析子类SimpleTypeParser的parse()方法

//子界的开始同步集合基本一样
final EnumSet<PascalTokenType> SIMPLE_TYPE_START_SET =
   3:        ConstantDefinitionsParser.CONSTANT_START_SET.clone();
static {
//枚举
// 枚举中间的枚举值分割符
//重复了,哥们,不过还是写上
   8:    }
   9:  
/**
    * 解析一个简单类型说明
    * @param token 当前Token
    * @return 简单类型说明(描述)
    * @throws Exception
    */
public TypeSpec parse(Token token)
throws Exception
  18:    {
//同步到简单类型起始处
  20:        token = synchronize(SIMPLE_TYPE_START_SET);
  21:  
switch ((PascalTokenType) token.getType()) {
case IDENTIFIER: {
  25:                String name = token.getText().toLowerCase();
  26:                SymTabEntry id = symTabStack.lookup(name);
  27:  
if (id != null) {
  29:                    Definition definition = id.getDefinition();
if (definition == DefinitionImpl.TYPE) {
  31:                        id.appendLineNumber(token.getLineNumber());
  32:                        token = nextToken(); 
//返回引用类型的说明
return id.getTypeSpec();
  35:                    }
if ((definition != CONSTANT) &&
  37:                             (definition != ENUMERATION_CONSTANT)) {
this);
  39:                        token = nextToken();  
return null;
  41:                    }
//子界如  monday..friday
  43:                        SubrangeTypeParser subrangeTypeParser =
this);
return subrangeTypeParser.parse(token);
  46:                    }
  47:                }
else {
this);
  50:                    token = nextToken(); 
return null;
  52:                }
  53:            }
//左括号意味着开始一个枚举
case LEFT_PAREN: {
  56:                EnumerationTypeParser enumerationTypeParser =
this);
return enumerationTypeParser.parse(token);
  59:            }
//不支持
case COMMA:
case SEMICOLON: {
this);
return null;
  65:            }
//默认子界,比如 'a' .. 'z'
default: {
  68:                SubrangeTypeParser subrangeTypeParser =
this);
return subrangeTypeParser.parse(token);
  71:            }
  72:        }
  73:    }

如果当前token是一个标识符,parse()方法必须判定它是否一个类型标识符,还是一个常量标识符或者枚举项标识符。对于类型标识符,方法返回对应符号表项的类型说明;而对于常量标识符或枚举项标识符,方法通过subrangeTypeParser.parse()返回其结果。

如果当前的token是一个左括号'(',parse()方法返回enumerationTypeParser.parse()解析后的结果。对于任何其它类型的token,方法直接通过subrangeTypeParser.parse()返回其结果。

清单9-24 展示了类型说明的解析器子类SubrangeTypeParser的parse()和checkValueType()方法。

public TypeSpec parse(Token token)
throws Exception
   3: {
   4:     TypeSpec subrangeType = TypeFactory.createType(SUBRANGE);
//下界
//上界
   7:  
//解析下界常量
   9:     Token constantToken = token;
  10:     ConstantDefinitionsParser constantParser =
this);
  12:     minValue = constantParser.parseConstant(token);
// 设置下界常量的类型说明
  14:     TypeSpec minType = constantToken.getType() == IDENTIFIER
  15:                            ? constantParser.getConstantType(constantToken)
  16:                            : constantParser.getConstantType(minValue);
  17:     minValue = checkValueType(constantToken, minValue, minType);
  18:     token = currentToken();
  19:     Boolean sawDotDot = false;
//检查子界中表示范围的两个点
if (token.getType() == DOT_DOT) {
  22:         token = nextToken();
  23:         sawDotDot = true;
  24:     }
  25:     TokenType tokenType = token.getType();
// 上界常量?
if (ConstantDefinitionsParser.CONSTANT_START_SET.contains(tokenType)) {
if (!sawDotDot) {
this);
  30:         }
// 解析上界
  32:         token = synchronize(ConstantDefinitionsParser.CONSTANT_START_SET);
  33:         constantToken = token;
  34:         maxValue = constantParser.parseConstant(token);
// 设置上界类型并检验是否与值匹配
  36:         TypeSpec maxType = constantToken.getType() == IDENTIFIER
  37:                            ? constantParser.getConstantType(constantToken)
  38:                            : constantParser.getConstantType(maxValue);
  39:         maxValue = checkValueType(constantToken, maxValue, maxType);
//下界和上界的类型是否都有了?
if ((minType == null) || (maxType == null)) {
this);
  43:         }
//下界和上界类型是否一致(因为没有类型兼容,所以直接使用的比较)
if (minType != maxType) {
this);
  47:         }
  48:  
//下界大于上界的情况,报错
if ((minValue != null) && (maxValue != null) &&
  51:                  ((Integer) minValue >= (Integer) maxValue)) {
this);
  53:         }
  54:     }
else {
this);
  57:     }
  58:  
  59:     subrangeType.setAttribute(SUBRANGE_BASE_TYPE, minType);
  60:     subrangeType.setAttribute(SUBRANGE_MIN_VALUE, minValue);
  61:     subrangeType.setAttribute(SUBRANGE_MAX_VALUE, maxValue);
  62:  
return subrangeType;
  64: }
  65:  
/**
 * 检查一个类型说明的值,是否匹配
 */
private Object checkValueType(Token token, Object value, TypeSpec type)
  70: {
if (type == null) {
return value;
  73:     }
if (type == Predefined.integerType) {
return value;
  76:     }
if (type == Predefined.charType) {
char ch = ((String) value).charAt(0);
return Character.getNumericValue(ch);
  80:     }
if (type.getForm() == ENUMERATION) {
return value;
  83:     }
else {
this);
return value;
  87:     }
  88: }

Parse()方法调用TypeFactory.createType()创建一个子界TypeSpec对象。它调用两次constantParser.parseConstant()解析被token '..' 分开的最小和最大常量值(即下界,上界)。方法调用checkValueType()检验常量值是否是整数,字符或者枚举量。它还检查两个值的类型是否一致以及下界值是否大于或等于上界值。如果一切OK,parse()将设置子界TypeSpec对象的SUBRANGE_BASE_TYPE, SUBRANGE_MIN_VALUE, 和SUBRANGE_MAX_VALUE 属性值

清单9-25 展示了类型说明解析子类EnumerationTypeParser的parse()和parseEnumerationIdentifier()方法。

extends TypeSpecificationParser
   2: {
protected EnumerationTypeParser(PascalParserTD parent)
   4:     {
super(parent);
   6:     }
//左括号之后,开始一个枚举的同步集
final EnumSet<PascalTokenType> ENUM_CONSTANT_START_SET =
   9:         EnumSet.of(IDENTIFIER, COMMA);
  10:  
//一个枚举定义后的同步集
final EnumSet<PascalTokenType> ENUM_DEFINITION_FOLLOW_SET =
  13:         EnumSet.of(RIGHT_PAREN, SEMICOLON);
static {
  15:         ENUM_DEFINITION_FOLLOW_SET.addAll(DeclarationsParser.VAR_START_SET);
  16:     }
  17:  
public TypeSpec parse(Token token)
throws Exception
  20:     {
  21:         TypeSpec enumerationType = TypeFactory.createType(ENUMERATION);
int value = -1;
new ArrayList<SymTabEntry>();
  24:  
// 吞噬掉开头的左括号。
  26:  
do {
  28:             token = synchronize(ENUM_CONSTANT_START_SET);
  29:             parseEnumerationIdentifier(token, ++value, enumerationType,
  30:                                        constants);
  31:  
  32:             token = currentToken();
  33:             TokenType tokenType = token.getType();
  34:  
// 枚举常量之间的逗号
if (tokenType == COMMA) {
  37:                 token = nextToken(); 
  38:  
if (ENUM_DEFINITION_FOLLOW_SET.contains(token.getType())) {
this);
  41:                 }
  42:             }
if (ENUM_CONSTANT_START_SET.contains(tokenType)) {
this);
  45:             }
while (!ENUM_DEFINITION_FOLLOW_SET.contains(token.getType()));
  47:  
// 结束的右括号
if (token.getType() == RIGHT_PAREN) {
  50:             token = nextToken();  
  51:         }
else {
this);
  54:         }
  55:  
  56:         enumerationType.setAttribute(ENUMERATION_CONSTANTS, constants);
return enumerationType;
  58:     }
  59:  
/**
     * 解析一个枚举标识符即 enumxx(a,b,c)中的a,b或者c
     * @param token 当前token
     * @param value 标识符的对应的整数索引值,以0起步
     * @param enumerationType 对应的枚举类型即enumxx
     * @param constants 枚举常量标识符的容器
     * @throws Exception
     */
int value,
  69:                                             TypeSpec enumerationType,
  70:                                             ArrayList<SymTabEntry> constants)
throws Exception
  72:     {
  73:         TokenType tokenType = token.getType();
  74:  
if (tokenType == IDENTIFIER) {
  76:             String name = token.getText().toLowerCase();
  77:             SymTabEntry constantId = symTabStack.lookupLocal(name);
//这个常量一定是没有定义过的
if (constantId != null) {
this);
  81:             }
else {
  83:                 constantId = symTabStack.enterLocal(name);
  84:                 constantId.setDefinition(ENUMERATION_CONSTANT);
  85:                 constantId.setTypeSpec(enumerationType);
  86:                 constantId.setAttribute(CONSTANT_VALUE, value);
  87:                 constantId.appendLineNumber(token.getLineNumber());
  88:                 constants.add(constantId);
  89:             }
  90:  
//继续
  92:         }
else {
this);
  95:         }
  96:     }
  97: }

Parse方法调用TypeFactory.createType() 创建一个枚举类型对象,接着循环调用parseEnumerationIdentifier()解析每个枚举常量并将其整数值加1。它设置枚举TypeSpec对象的ENUMERATION_CONSTANTS属性为各个枚举常量标识符对应的符号表项形成的列表。整数值以0开始,每次成功处理一个枚举常量后增1。

方法parseEnumerationIdentifier()将每个枚举常量标识符放入局部符号表中。它设置表项的定义为DefinitionImpl.ENUMERATION_CONSTANT,其类型说明为新的枚举TypeSpec对象,还有它的CONSTANT_VALUE属性值为整数。它把这个符号表项加到constants列表中。

数组类型(Array Types)

一个Pascal数组说明包含两部分:

  • 在关键字ARRAY之后,一个以逗号分开的一个或多个索引类型说明形成的列表,每个(索引类型)代表一个维度,它们被方括号[]括起来。
  • 在关键字OF后面,是数组元素的类型说明。

多维的数组尤其有意思。在Pascal中,多个索引类型说明可以列在一起,比如

ARRAY [1..3, 'a'..'z', boolean] OF real 相当于

ARRAY [1..3] OF

    ARRAY ['a'..'z'] OF

        ARRAY [boolean] OF real;

>> 继续第九章

相关文章:

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