实验三 LR(1)分析法
构造 LR(1)分析程序,利用它进行语法分析,判断给出的符号串是否为该文 法识别的句子,了解 LR(K)分析方法是严格的从左向右扫描,和自底向上的 语法分析方法。
二、实验内容
对下列文法,用 LR(1)分析法对任意输入的符号串进行分析: (1)E-> E+T
(2)E->T
(3)T-> T*F
(4)T->F (5)F-> (E) (6)F-> i
三、LR(1)分析法实验设计思想及算法
(1)总控程序,也可以称为驱动程序。对所有的 LR 分析器总控程序都是相同的。 (2)分析表或分析函数,不同的文法分析表将不同,同一个文法采用的 LR 分析器 不同时,分析表将不同,分析表又可以分为动作表(ACTION)和状态转换(GOTO) 表两个部分,它们都可用二维数组表示。 (3)分析栈,包括文法符号栈和相应的状态栈,它们均是先进后出栈。 分析器的动作就是由栈顶状态和当前输入符号所决定。
LR 分析器由三个部分组成:
16
其中:SP 为栈指针,S[i]为状态栈,X[i]为文法符号栈。状态转换表用 GOTO[i,X]=j 表示,规定当栈顶状态为 i,遇到当前文法符号为 X 时应 转向状态 j,X 为终结符或非终结符。
ACTION[i,a]规定了栈顶状态为 i 时遇到输入符号 a 应执行。动作有四种 可能:
(1)移进:
action[i,a]= Sj:状态 j 移入到状态栈,把 a 移入到文法符号栈,其中 i,j 表
示状态号。 (2)归约:
action[i,a]=rk:当在栈顶形成句柄时,则归约为相应的非终结符 A,即文 法中有 A- B 的产生式,若 B 的长度为 R(即|B|=R),则从状态栈和文法符号栈中 自顶向下去掉 R 个符号,即栈指针 SP 减去 R,并把 A 移入文法符号栈内, j=GOTO[i,A]移进状态栈,其中 i 为修改指针后的栈顶状态。
(3)接受 acc:
当归约到文法符号栈中只剩文法的开始符号 S 时,并且输入符号串已结束即 当前输入符是'#',则为分析成功。
(4)报错:
当遇到状态栈顶为某一状态下出现不该遇到的文法符号时,则报错,说明输 入端不是该文法能接受的符号串。
17
四、实验要求
1、编程时注意编程风格:空行的使用、注释的使用、缩进的使用等。 2、如果遇到错误的表达式,应输出错误提示信息。 3、程序输入/输出实例: 输入一以#结束的符号串(包括+*()i#):在此位置输入符号串 输出过程如下:
步骤 状态栈 符号栈 剩余输入串 动作
1 0 # i+i*i# 移进
i+i*i 的 LR 分析过程 | ||||
步骤 | 状态栈 | 符号栈 | 输入串 | 动作说明 |
1 | 0 | # | i+i*i# | ACTION[0,i]=S5,状态 5 入栈 |
2 | 05 | #i | +i*i# | r6: F→i 归约,GOTO(0,F)=3 入栈 |
3 | 03 | #F | +i*i# | r4: T→F 归约,GOTO(0,T)=3 入栈 |
4 | 02 | #T | +i*i# | r2: E→T 归约,GOTO(0,E)=1 入栈 |
18
5 | 01 | #E | +i*i# | ACTION[1,+]=S6,状态 6 入栈 |
6 | 016 | #E+ | i*i# | ACTION[6,i]=S5,状态 5 入栈 |
7 | 0165 | #E+i | *i# | r6: F→i 归约,GOTO(6,F)=3 入栈 |
8 | 0163 | #E+F | *i# | r4: T→F 归约,GOTO(6,T)=9 入栈 |
9 | 0169 | #E+T | *i# | ACTION[9,*]=S7,状态 7 入栈 |
10 | 01697 | #E+T* | i# | ACTION[7,i]=S5,状态 5 入栈 |
11 | 016975 | #E+T*i | # | r6:F→i 归约,GOTO(7,F)=10 入栈 |
12 | 0169710 | #E+T*F | # | r3: T→T*F 归约,GOTO(6,T)=9 入栈 |
13 | 0169 | #E+T | # | r1:E→E+T,GOTO(0,E)=1 入栈 |
14 | 01 | #E | # | Acc:分析成功 |
4、输入符号串为非法符号串(或者为合法符号串)
算术表达式文法的 LR 分析表 | |||||||||
状 态 | ACTION | GOTO | |||||||
i | + | * | ( | ) | # | E | T | F | |
0 | S5 | S4 | 1 | 2 | 3 | ||||
1 | S6 | acc | |||||||
2 | r2 | S7 | r2 | r2 | |||||
3 | r4 | r4 | r4 | r4 | |||||
4 | S5 | S4 | 8 | 2 | 3 | ||||
5 | r6 | r6 | r6 | r6 | |||||
6 | S5 | S4 | 9 | 3 | |||||
7 | S5 | S4 | 10 | ||||||
8 | S6 | S11 | |||||||
9 | r1 | S7 | r1 | r1 | |||||
10 | r3 | r3 | r3 | r3 | |||||
11 | r5 | r5 | r5 | r5 |
五、实验步骤
1、根据流程图编写出各个模块的源程序代码上机调试。
19
2、编制好源程序后,设计若干用例对系统进行全面的上机测试,并通过所设计 的 LR(1)语法分析程序;直至能够得到完全满意的结果。
3、书写实验报告 ;实验报告正文的内容:
-
描述 LR(1)语法分析程序的设计思想。
-
程序结构描述:函数调用格式、参数含义、返回值描述、函数功能;函数
之间的调用关系图。
-
详细的算法描述(程序执行流程图)。
-
给出软件的测试方法和测试结果。
实验总结 (设计的特点、不足、收获与体会)。
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Q3 {
StringBuilder input = new StringBuilder();
Deque<Integer> sStack = new LinkedList<>();
Deque<Character> cStack = new LinkedList<>();
void start() throws IOException {
InputStreamReader reader = new InputStreamReader(Q2.class.getResourceAsStream("Q2"));
for(int ch; (ch = reader.read()) != -1; ){
input.append((char)ch);
}
sStack.add(0);
cStack.add('#');
int s = sStack.getLast();
char a = input.charAt(0);
while(true){
String temp = ACTION.get(s).get(a);
if(temp == null){
print("ERROR");
break;
}else if(temp.startsWith("r")){
print("");
int index = Integer.parseInt(temp.substring(1));
int length = production[index][1].length();
while(length-- != 0){
sStack.removeLast(); cStack.removeLast();
}
cStack.add(production[index][0].charAt(0));
if(GOTO.get(sStack.getLast()).get(cStack.getLast()) == null){
print("ERROR");
break;
}
s = GOTO.get(sStack.getLast()).get(cStack.getLast());
sStack.add(s);
}else if(temp.startsWith("S")){
print("");
sStack.add(Integer.parseInt(temp.substring(1)));
s = sStack.getLast();
cStack.add(a);
input.deleteCharAt(0);
a = input.charAt(0);
}else {
print("SUCCESS");
break;
}
}
}
void print(String msg){
if("ERROR".equals(msg)){
System.out.println(msg);
}else if("SUCCESS".equals(msg)){
System.out.println(sStack.toString() + '\t' + cStack.toString() + '\t' + input.toString() + '\t' + msg);
}else {
System.out.println(sStack.toString() + '\t' + cStack.toString() + '\t' + input.toString());
}
}
static List<Map<Character, String>> ACTION = new ArrayList<>();
static List<Map<Character, Integer>> GOTO = new ArrayList<>();
static String[][] production = null;
public static void main(String[] args) throws IOException {
production = new String[][] {{}, {"E", "E+T"}, {"E", "T"}, {"T", "T*F"}, {"T", "F"}, {"F", "(E)"}, {"F", "i"}};
ACTION.add(new HashMap<Character, String>(){{ put('i', "S5"); put('(', "S4"); }});
ACTION.add(new HashMap<Character, String>(){{ put('+', "S6"); put('#', "acc"); }});
ACTION.add(new HashMap<Character, String>(){{ put('+', "r2"); put('*', "S7"); put(')', "r2"); put('#', "r2"); }});
ACTION.add(new HashMap<Character, String>(){{ put('+', "r4"); put('*', "r4"); put(')', "r4"); put('#', "r4"); }});
ACTION.add(ACTION.get(0));
ACTION.add(new HashMap<Character, String>(){{ put('+', "r6"); put('*', "r6"); put(')', "r6"); put('#', "r6"); }});
ACTION.add(ACTION.get(0));
ACTION.add(ACTION.get(0));
ACTION.add(new HashMap<Character, String>(){{ put('+', "S6"); put(')', "S11"); }});
ACTION.add(new HashMap<Character, String>(){{ put('+', "r1"); put('*', "S7"); put(')', "r1"); put('#', "r1"); }});
ACTION.add(new HashMap<Character, String>(){{ put('+', "r3"); put('*', "r3"); put(')', "r3"); put('#', "r3"); }});
ACTION.add(new HashMap<Character, String>(){{ put('+', "r5"); put('*', "S5"); put(')', "r5"); put('#', "r5"); }});
GOTO.add(new HashMap<Character, Integer>(){{ put('E', 1); put('T', 2); put('F', 3); }});
GOTO.add(new HashMap<>());
GOTO.add(new HashMap<>());
GOTO.add(new HashMap<>());
GOTO.add(new HashMap<Character, Integer>(){{ put('E', 8); put('T', 2); put('F', 3); }});
GOTO.add(new HashMap<>());
GOTO.add(new HashMap<Character, Integer>(){{ put('T', 9); put('F', 3); }});
GOTO.add(new HashMap<Character, Integer>(){{ put('F', 10); }});
GOTO.add(new HashMap<>());
GOTO.add(new HashMap<>());
GOTO.add(new HashMap<>());
GOTO.add(new HashMap<>());
Q3 q3 = new Q3();
q3.start();
}
}