一、 实验目的
构造 LR(1)分析程序,利用它进行语法分析,判断给出的符号串是否为该文法识别的句子,了解 LR(K)分析方法是严格的从左向右扫描,和自底向上的语法分析方法。
二、 描述 LR(1)语法分析程序的设计思想
1. 拓广文法
假定文法是一个以为开始符号的文法,我们构造一个,它包含了整个,但它引进了一个不出现在中的非终结符,并加进一个新产生式,而是的开始符号。那么,我们称是的拓广文法。这样,便会有一个仅含项目的状态,这便是唯一的接受态。
2. 提取所有有效识别活前缀的式子:
形式上我们说一个LR(1)项目对于活前缀是有效的,如果存在规范推导
其中,① ;② a是的 第一个符号,或者a为#而 为 。
若 对活前缀 是有效的,则对于每个形如 的产生式, 对任何的 , 对 也是有效的。
3. 构造项目集I的闭包CLOSURE(I):
假定是一个项目集,它的闭包可按如下方式构造:
(1) 的任何项目都属于。
(2) 若项目属于,是一个产生式,那么,对于中的每个终结符,如果原来不在中,则把它加进去。
(3) 重复执行步骤2,直至不再增大为止。
4. 构造GO(I, X)
令是一个项目集,是一个文法符号,函数定义为
其中
5. 构造LR(1)项目集族C
关于文法的LR(1)项目集族的构造算法是:
6.分析表构造:
假设,令每个的下标为分析表的状态,令含有的的为分析器的初态。动作和状态转换可构造如下:
(1) 若项目属于且,为终结符,则置为“把状态和符号移入栈”,简记为""。
(2) 若项目属于,则置为“用产生式规约”,简记为"",其中假定为文法的第个产生式。
(3) 若项目属于,则置为“接受”,简记为""。
(4) 若,则置。
(5) 分析表中凡不能用规则1至4填入信息的空白栏均填上“出错标志”。
三、 LR分析器详细的算法描述
LR分析器的总控程序本身的工作是比较简单的。它的任何一步只需按照栈顶状态和现行输入符号执行所规定的动作。不管什么分析表,总控程序都是一样的工作。
一个LR分析器的工作过程可以看成是栈里的状态序列、已规约串和输入串所构成的三元式的变化过程。分析开始时的初始三元式是
其中,为分析器的初态; 为句子的左括号; 为输入串;其后的为结束符(句子右括号)。分析过程的每步结果可以表示为
分析器的下一步动作是由栈顶状态和现行输入符号所唯一确定的。即,执行所规定的动作。经执行每种动作后,三元式的变化情况是:
(1) 若为移进,且,则三元式变成
(2) 若为使用规约,则三元式变成
其中,,为的长度,
(3) 若为接受,则三元式不再变化,宣布分析成功
(4) 若为报错,则三元式的变化过程终止,报告错误。
用流程图表示如下:
四、 源代码
(1) LR1Project.java
package exp3;
import exp2.grammer.symbol.GrammerSymbol;
import exp2.grammer.symbol.GrammerSymbols;
import java.util.Objects;
/**
* LR(1)项目
*
* @version 2020-06-07
*/
public class LR1Project implements Comparable {
/** 产生式左部 */
public final GrammerSymbol left;
/** 产生式右部 */
public final GrammerSymbols right;
/** 圆点的位置 */
public final int pos;
/** 向前搜索的符号 */
public final GrammerSymbol forward;
/**
* 构造LR(1)项目
*
* @param left 产生式左部
* @param right 产生式右部
* @param pos 圆点的位置
* @param forward 向前搜索符
*/
public LR1Project(GrammerSymbol left, GrammerSymbols right, int pos, GrammerSymbol forward) {
this.left = left;
this.right = right;
this.pos = pos;
this.forward = forward;
}
@Override
public String toString() {
return "[" + left +
"->" +
right.subSequence(0, pos) +
"·" +
right.subSequence(pos) +
", " + forward +
']';
}
@Override
public int compareTo(LR1Project o) {
int cmp = this.left.compareTo(o.left);
if (cmp != 0) return cmp;
cmp = this.right.compareTo(o.right);
if (cmp != 0) return cmp;
cmp = Integer.compare(this.pos, o.pos);
if (cmp != 0) return cmp;
return this.forward.compareTo(o.forward);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LR1Project that = (LR1Project) o;
return this.toString().equals(that.toString());
}
@Override
public int hashCode() {
return Objects.hash(left, right, pos, forward);
}
}
(2) LR1Action.java
package exp3;
import java.util.Objects;
/**
* LR(1)文法的动作
*
* @version 2020-06-14
*/
public class LR1Action {
/** 动作类型编号,0为s,1为r,2为acc */
public final static int PUSHSTACK = 0;
public final static int REDUCE = 1;
public final static int ACCEPT = 2;
/** 动作类型 */
public int type;
/** 当为进栈操作时,表示把状态j放入栈中;当为规约操作时,表示使用第j个产生式进行规约 */
public int j;
/**
* 根据类型构造动作,应当为接受动作的构造方法,也就是type应为2
*
* @param type 类型,应为2,也就是ACCEPT
*/
public LR1Action(int type) {
this.type = type;
}
/**
* 根据类型构造动作,不应当为接受动作的构造方法
*
* @param type 类型,应为0和1,也就是PUSHSTACK和REDUCE
* @param j 当为进栈操作时,表示把状态j放入栈中;当为规约操作时,表示使用第j个产生式进行规约
*/
public LR1Action(int type, int j) {
this.type = type;
this.j = j;
}
@Override
public String toString() {
switch (this.type) {
case PUSHSTACK: return "s" + this.j;
case REDUCE: return "r" + this.j;
case ACCEPT: return "acc";
default: return "error";
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LR1Action lr1Action = (LR1Action) o;
return type == lr1Action.type &&
j == lr1Action.j;
}
@Override
public int hashCode() {
return Objects.hash(type, j);
}
}
(3) LR1Grammer.java
package exp3;
import exp2.grammer.Grammer;
import exp2.grammer.exception.GrammerException;
import exp2.grammer.symbol.*;
import exp2.util.Pair;
import java.util.*;
import static exp2.grammer.symbol.GrammerSymbol.*;
import static java.util.AbstractMap.SimpleImmutableEntry;
/**
* LR(1)文法
*
* @version 2020-06-14
*/
public class LR1Grammer extends Grammer {
/* 文法的Fisrt集合 */
Map> FirstSets;
/* 文法的Follow集合 */
Map> FollowSets;
/** LR(1)文法中的所有项目 */
Set allProjects;
/** LR(1)文法的项目集族 */
List> C;
/** ACTION表 */
Map> ACTION;
/** GOTO表 */
Map> GOTO;
/** (有序的)产生式列表 */
List> ProdList;
/**
* 从文法产生式输入构造一个文法
*
* @param src 文法输入
*/
public LR1Grammer(String src) {
this(src.split("(\r\n)|(\n)|(\r)"));
}
/**
* 从文法产生式字符串数组构造一个文法
*
* @param srcs 文法字符串数组
*/
public LR1Grammer(String[] srcs) {
super(srcs);
this.FirstSets = this.generateFirstSets();
this.FollowSets = this.generateFollowSets();
this.allProjects = this.generateProjects();
this.C = this.generateC();
this.ProdList = this.generateProdList();
SimpleImmutableEntry
(4) LR1Parser.java
package exp3;
import exp2.grammer.Grammer;
import exp2.grammer.LL1Grammer;
import exp2.grammer.exception.GrammerException;
import exp2.grammer.symbol.GrammerSymbol;
import exp2.grammer.symbol.GrammerSymbols;
import exp2.util.Pair;
import exp2.util.Quartet;
import java.util.*;
import static exp3.LR1Action.*;
/**
* LR(1)语法分析器
*
* @version 2020-06-15
*/
public class LR1Parser {
/* 某个LL(1)文法 */
final LR1Grammer grammer;
/**
* 从某个一个存在的LL(1)文法构造LL(1)预测分析程序
*
* @param grammer 已经存在的LL(1)文法
*/
public LR1Parser(LR1Grammer grammer) {
this.grammer = grammer;
}
/**
* 从某个一个存在的文法构造LL(1)预测分析程序
*
* @param grammer 已经存在的文法
* @throws GrammerException 如果这个文法不是LL(1)的
*/
public LR1Parser(Grammer grammer) { this.grammer = new LR1Grammer(grammer); }
/**
* 采用给定的LL(1)文法{@link #grammer}分析字符串
*
* @param string 要分析的字符串
* @return 分析结果
*/
public List> parseString(String string) {
return parseGrammerSymbols(new GrammerSymbols(string));
}
/**
* 采用给定的LL(1)文法{@link #grammer}分析输入的文法符号序列
*
* @param input 要分析的文法符号序列
* @return 分析结果
*/
public List> parseGrammerSymbols(GrammerSymbols input) {
/* 状态栈, 符号栈, 剩余输入串, 动作 */
List> snap = new LinkedList<>();
String inputString = input.toString();
Stack symbolStack = new Stack<>();
Stack stateStack = new Stack<>();
GrammerSymbol EOI = new GrammerSymbol("#");
GrammerSymbols Epsilon = new GrammerSymbols(Collections.singletonList(GrammerSymbol.GS_EPSILON));
/* 把#推入符号栈中 */
symbolStack.add(EOI);
/* 把初态推入状态栈中,初态按照实现,应当为项目集的第一个 */
stateStack.add(0);
snap.add(new Quartet<>(stateStack.toString(), symbolStack.toString(), inputString, "初始化"));
int index = 0;
GrammerSymbol a;
Set curState;
int curStateID;
LR1Action action;
String actionString;
boolean flag = true;
while (flag) {
/* 栈顶状态 */
curState = this.grammer.C.get(stateStack.peek());
/* 栈顶状态的标号 */
curStateID = this.grammer.C.indexOf(curState);
/* 现行输入符号a */
a = input.get(index);
/* 目前的动作 */
action = this.grammer.ACTION.get(curStateID).get(a);
switch (action.type) {
case PUSHSTACK: {
actionString = "状态" + action.j + "和符号" + a + "入栈";
stateStack.add(action.j);
symbolStack.add(a);
++index;
break;
}
case REDUCE: {
Pair prod = this.grammer.ProdList.get(action.j);
actionString = "用" + prod.value1 + "->" + prod.value2 +"规约";
int r = prod.value2.length();
for (int i = r-1; i >= 0; --i) {
GrammerSymbol curs = prod.value2.get(i);
GrammerSymbol symbolStackPeek = symbolStack.pop();
if (!curs.equals(symbolStackPeek)) {
flag = false;
actionString = "分析错误";
break;
}
stateStack.pop();
}
if (!flag) break;
symbolStack.add(prod.value1);
stateStack.add(this.grammer.GOTO.get(stateStack.peek()).get(prod.value1));
break;
}
case ACCEPT: {
flag = false;
actionString = "分析成功";
break;
}
default: {
flag = false;
actionString = "分析错误";
break;
}
}
/* 记录结果 */
snap.add(new Quartet<>(stateStack.toString(), symbolStack.toString(), inputString.substring(index), actionString));
}
return snap;
}
public LR1Grammer getGrammer() {
return grammer;
}
public static void main(String[] args) {
String[] srcs = {
"S'->S",
"S->BB",
"B->aB",
"B->b"
};
LR1Grammer lr1Grammer = new LR1Grammer(srcs);
LR1Parser parser = new LR1Parser(lr1Grammer);
List> snap = parser.parseString("aabab#");
System.out.println(snap);
System.out.println(snap.size());
}
}