续 第一部分
解析FOR语句
Pascal FOR语句稍微有些麻烦。语句解析器子类ForStatementParser的parse()方法解析如下语句:
FOR k := j TO 5 DO n := k
清单7-11 展示了ForStatementParser中的parse()方法(这个代码多了起来)。
// TO和DOWNTO同步集合 for expr ->to|downto CONST do stmt
final EnumSet<PascalTokenType> TO_DOWNTO_SET =
3: ExpressionParser.EXPR_START_SET.clone();
static {
5: TO_DOWNTO_SET.add(TO);
6: TO_DOWNTO_SET.add(DOWNTO);
7: TO_DOWNTO_SET.addAll(StatementParser.STMT_FOLLOW_SET);
8: }
// DO同步集合for expr to|downto CONST ->do stmt
final EnumSet<PascalTokenType> DO_SET =
11: StatementParser.STMT_START_SET.clone();
static {
13: DO_SET.add(DO);
14: DO_SET.addAll(StatementParser.STMT_FOLLOW_SET);
15: }
//发生的变量节点拷贝可以认为是源节点和拷贝节点树中关系不一样,但是一些属性
//比如对应的符号表项还是一样的引用。
public ICodeNode parse(Token token)
throws Exception
20: {
21: token = nextToken();
22: Token targetToken = token;
//参见图7-11
24: ICodeNode compoundNode = ICodeFactory.createICodeNode(COMPOUND);
25: ICodeNode loopNode = ICodeFactory.createICodeNode(LOOP);
26: ICodeNode testNode = ICodeFactory.createICodeNode(TEST);
// 初始赋值语句,调用一次
28: AssignmentStatementParser assignmentParser =
this);
30: ICodeNode initAssignNode = assignmentParser.parse(token);
31: setLineNumber(initAssignNode, targetToken);
32: compoundNode.addChild(initAssignNode);
//循环主体的复合节点
34: compoundNode.addChild(loopNode);
//同步在TO/DOWNTO处
36: token = synchronize(TO_DOWNTO_SET);
37: TokenType direction = token.getType();
if ((direction == TO) || (direction == DOWNTO)) {
39: token = nextToken();
40: }
else {
42: direction = TO;
this);
44: }
//根据TO/DOWNTO创建判断表达式GT或LT
46: ICodeNode relOpNode = ICodeFactory.createICodeNode(direction == TO
47: ? GT : LT);
//拷贝初始变量语句中的左边变量成为关系表达式的左边
49: ICodeNode controlVarNode = initAssignNode.getChildren().get(0);
50: relOpNode.addChild(controlVarNode.copy());
//TO或DOWNTO的限值表达式,成为关系表达式的右边
this);
53: relOpNode.addChild(expressionParser.parse(token));
//loop-->test-->relop三层
55: testNode.addChild(relOpNode);
56: loopNode.addChild(testNode);
// 在DO处同步
58: token = synchronize(DO_SET);
if (token.getType() == DO) {
60: token = nextToken();
61: }
else {
this);
64: }
//循环的主体语句
this);
67: loopNode.addChild(statementParser.parse(token));
//创建一个根据TO|DOWNTO来递增或递减控制变量的表达式。控制变量:=控制变量+/- 1;
69: ICodeNode nextAssignNode = ICodeFactory.createICodeNode(ASSIGN);
70: nextAssignNode.addChild(controlVarNode.copy());
71: ICodeNode arithOpNode = ICodeFactory.createICodeNode(direction == TO
72: ? ADD : SUBTRACT);
//递减发生在控制变量上,要拷贝。
74: arithOpNode.addChild(controlVarNode.copy());
75: ICodeNode oneNode = ICodeFactory.createICodeNode(INTEGER_CONSTANT);
76: oneNode.setAttribute(VALUE, 1);
77: arithOpNode.addChild(oneNode);
//将递增/递减后的值付给控制变量
79: nextAssignNode.addChild(arithOpNode);
80: loopNode.addChild(nextAssignNode);
81: setLineNumber(nextAssignNode, targetToken);
return compoundNode;
83: }
|
设计笔记 |
| LOOP节点容许中间码以一种语言无关的方式匹配各种不同花样的循环结构(WHILE/DO WHILE/REPEAT/FOR) |
解析IF语句
IF (i = j) THEN
t := 200
ELSE
f := -200;
清单7-14展示了IfStatementParser的parse()方法
// THEN的同步集合
final EnumSet<PascalTokenType> THEN_SET =
3: StatementParser.STMT_START_SET.clone();
static {
5: THEN_SET.add(THEN);
6: THEN_SET.addAll(StatementParser.STMT_FOLLOW_SET);
7: }
8:
public ICodeNode parse(Token token)
throws Exception
11: {
12: token = nextToken();
13: ICodeNode ifNode = ICodeFactory.createICodeNode(ICodeNodeTypeImpl.IF);
//IF语句的条件表达式
this);
16: ifNode.addChild(expressionParser.parse(token));
// THEN处同步一下
18: token = synchronize(THEN_SET);
if (token.getType() == THEN) {
20: token = nextToken();
21: }
else {
this);
24: }
//THEN后的语句
this);
27: ifNode.addChild(statementParser.parse(token));
28: token = currentToken();
//试探后没有ELSE语句
if (token.getType() == ELSE) {
31: token = nextToken();
32: ifNode.addChild(statementParser.parse(token));
33: }
return ifNode;
35: }
在Eclipse中运行Pascal,使用参数"compile -i if.txt",查看正确的输出结果。使用参数"compile -i iferrors.txt",查看带错误的输出结果。这里省略输出结果。做个补充演示:
按照左边图设置之后看运行输出,留意级联的IF THEN ELSE语句。在每个ELSE IF 分支处,嵌套的IF语句被当做另一个语句。(这个特性跟Java的一样)。
著名的"dangling ELSE"很容易出问题(dangling else意思说如果if xxx then yyy if mmm then nnn else kkk,这儿出现了else到底是被解释成第一个if语句的一部分还是第二个if语句的一部分?摇摆不定啊,通用的做法是紧贴近最里层的if,即第二个if)。在源文件(if.txt)第17行处,ELSE可以跟第一个和第二个THEN配对。遵照语法图7-1,IF语句
IF (j = 2) THEN t := 500 ELSE f := -500
是THEN语句的嵌套子句,即 IF (i = 1) THEN 嵌套子句。因此,ELSE与第二个THEN配对。
解析CASE语句
CASE语句是Pascal控制语句解析中最有挑战的一个。语句解析器子类CaseStatementParser解析CASE语句并生成它的分析树,比如语句:
CASE i+1 OF
1: j := i;
4: j := 4*i;
5, 2, 3: j := 523*i;
END
//一个CASE条件分支的起始同步集合
final EnumSet<PascalTokenType> CONSTANT_START_SET =
3: EnumSet.of(IDENTIFIER, INTEGER, PLUS, MINUS, STRING);
4:
// OF同步集合
final EnumSet<PascalTokenType> OF_SET =
7: CONSTANT_START_SET.clone();
static {
9: OF_SET.add(OF);
10: OF_SET.addAll(StatementParser.STMT_FOLLOW_SET);
11: }
public ICodeNode parse(Token token)
throws Exception
14: {
//干掉开头的CASE
16: token = nextToken();
17: ICodeNode selectNode = ICodeFactory.createICodeNode(SELECT);
//CASE表达式
this);
20: selectNode.addChild(expressionParser.parse(token));
//同步OF
22: token = synchronize(OF_SET);
if (token.getType() == OF) {
// consume the OF
25: }
else {
this);
28: }
//分支选项集合
new HashSet<Object>();
instanceof EofToken) && (token.getType() != END)) {
32: selectNode.addChild(parseBranch(token, constantSet));
33: token = currentToken();
34: TokenType tokenType = token.getType();
// 一个分支以分号';'结束
if (tokenType == SEMICOLON) {
37: token = nextToken();
38: }
if (CONSTANT_START_SET.contains(tokenType)) {
this);
41: }
42: }
//CASE expr OF branches END
if (token.getType() == END) {
45: token = nextToken();
46: }
else {
this);
49: }
50:
return selectNode;
52: }
private ICodeNode parseBranch(Token token, HashSet<Object> constantSet)
throws Exception
3: {
//每个分支都有一个SELECT_BRANCH节点,constantsNode是第一个孩子
5: ICodeNode branchNode = ICodeFactory.createICodeNode(SELECT_BRANCH);
6: ICodeNode constantsNode =ICodeFactory.createICodeNode(SELECT_CONSTANTS);
7: branchNode.addChild(constantsNode);
//解析常量列表,每个常量节点是是constantNode的子节点)
9: parseConstantList(token, constantsNode, constantSet);
//查找分支冒号
11: token = currentToken();
if (token.getType() == COLON) {
13: token = nextToken();
14: }
else {
this);
17: }
//冒号:后面的语句
this);
20: branchNode.addChild(statementParser.parse(token));
return branchNode;
22: }
void parseConstantList(Token token, ICodeNode constantsNode,
2: HashSet<Object> constantSet)
throws Exception
4: {
// 遍历每一个常量
while (CONSTANT_START_SET.contains(token.getType())) {
7: constantsNode.addChild(parseConstant(token, constantSet));
8: token = synchronize(COMMA_SET);
// 常量以','隔开
if (token.getType() == COMMA) {
11: token = nextToken();
12: }
if (CONSTANT_START_SET.contains(token.getType())) {
this);
15: }
16: }
17: }
解析常量的代码过于简单,跟解析常量token类似,这里不演示了,有兴趣可以看源代码的第153到268行
- 一个分支常量可以是一个整数或者单个的字符,比如1,2,3: 或 'a','b','c:
- CASE语句中的分支常量不能被使用多次,只能使用一次。
在Eclipse中运行Pascal,使用参数"compile -i case.txt",查看正确的输出结果。使用参数"compile -i caseerrors.txt",查看带错误的输出结果。这里省略输出结果。
本章完.