實驗目的
根據給出的文法編制LR(1)分析程序,以便對任意輸入的符號串進行分析。本次實驗的目的主要是加深對LR(1)分析法的理解。
實驗環境
①開發語言:java
②開發環境:jdk1.8
功能描述
利用LR(1)分析表,對輸入符號串自底而上的分析過程。輸入要分析的字符串,輸出解析結果到結果文件。如果成功輸出success,如果失敗輸出錯誤信息
分析表構造過程
closure和goto函數
文法
Algol,為算法語言(ALGOrithmic Language)的縮寫,是計算機發展史上首批產生的高級程式語言家族。當時還是晶體管計算機流行的時代,由於ALGOL語句和普通語言表達式接近,更適於數值計算,所以ALGOL多用於科學計算機。
我用的是類Algol文法
program → block
block → { stmts }
stmts → stmt stmts | e
stmt → id= expr ;
| if ( bool ) stmt
| if ( bool) stmt else stmt
| while (bool) stmt
| do stmt while (bool ) ;
| break ;
| block
bool → expr < expr
| expr <= expr
| expr > expr
| expr >= expr
| expr
expr → expr + term
| expr - term
| term
term → term * factor
| term / factor
| factor
factor → ( expr ) | id| num
程序設計思路
1.手動畫出LR(1)表。其實我已經寫出來得到first集的函數,但是構造LR(1)的算法邏輯好復雜啊。因為這個實驗是針對特定的文法,就沒有專門去寫構造LR(1)表的函數。但是這個58個狀態畫的真的累,以后考慮下寫寫構造LR(1)表的算法。
2.定義一個用於描述規則的類type,用於規約的時候查詢。有三個屬性right
,left
,pop
- right:String存放規則的左邊
- left:String存放規則的右邊
- pop:int規約時要彈出幾個狀態
3.定義一個Map全局變量存放規則的集合rule
定義存放action表的MapMaction
和存放goto表的MapMgoto
,key是狀態,value是輸入非終結符或終結符對應的操作。
如當前狀態2,遇到'>'跳到狀態15,則Map<2,Map<">",15>>
定義狀態棧和符號棧的全局變量
用list存放非終結符和終結符,方便查詢
static Map<Integer,type> rule=new HashMap<>();//規則
static Stack<Integer> stack=new Stack<>();//狀態棧
static Stack<String> stack1=new Stack<>();//符號棧
static List<String> NFinal=new ArrayList<String>();//非終結符
static List<String> Final=new ArrayList<String>();//終結符
static Map<Integer,Map<String,String>> Maction=new HashMap<>();//LRaction表
static Map<Integer,Map<String,String>> Mgoto=new HashMap<>();//LRgoto表
4.程序結構描述:函數調用格式、參數含義、返回值描述、函數功能
- getActionMap()
參數:無
返回值:無
功能:初始化action表 - getGotoMap()
參數:無
返回值:無
功能:初始化goto表 - newrule()
參數:無
返回值:無
功能:初始化規則集合 - newtype(String right,String left,int pop,int key)
參數:right,left,pop用來new一個type,將參數key作為rule的key,type作為value
返回值:無
功能:初始化一條規則 - isFinal(String a)
參數:a輸入串
返回值:一個終結符/NULL
功能:得到當前句子的第一個終結符,若有返回第一個終結符,若無返回NULL - isNFinal(String a)
參數:a輸入串
返回值:一個非終結符/NULL
功能:得到當前句子的第一個非終結符,若有返回第一個終結符,若無返回NULL - getTag()
參數:無
返回值:無
功能:初始化終結符和非終結符list - cifa(String s1)
參數:s1為待分析的字符串
返回值:無
功能:進行對s1的詞法分析 - print(int step,String stack,String stack1,String s1,String AorG,int i)
參數:step
當前步驟,stack
狀態棧,stack1
符號棧,s1
輸入串,AorG
操作,i
是action還是goto操作
返回值:無
功能:將分析結果輸出到輸出文件 - getinput()
參數:無
返回值:String
功能:輸入要分析的字符串
5.流程圖
程序結構描寫
(1)初始化action表(部分)
public static void getActionMap()//初始化action
{
Map<String,String> map=new HashMap<>();
map.put("{", "s4");
Maction.put(1, map);
map=new HashMap<>();
map.put("$","acc");
Maction.put(2, map);
map=new HashMap<>();
map.put("$","r1");
Maction.put(3, map);
map=new HashMap<>();
map.put("{","s4");
map.put("id","s7");
map.put("if","s8");
map.put("while","s9");
map.put("do","s10");
map.put("break","s11");
Maction.put(4, map);
}
(2)初始化goto表(部分)
public static void getGotoMap()//初始化goto
{
Map<String,String> map=new HashMap<>();
map.put("program","2");
map.put("block","2");
Mgoto.put(1, map);
map=new HashMap<>();
map.put("block","12");
map.put("stst","5");
map.put("stmt","6");
Mgoto.put(4, map);
map=new HashMap<>();
map.put("block","12");
map.put("stst","14");
map.put("stmt","6");
Mgoto.put(6, map);
}
(3)初始化規則集
public static void newrule()//初始化規則
{
newtype("program","block",1,1);
newtype("block","{stst}",3,2);
newtype("stst","stmtstst",2,3);
newtype("stmt","id=expr;",4,4);
newtype("stmt","if(bool)stmt",5,5);
newtype("stmt","if(bool)elsestmt",6,6);
newtype("stmt","while(bool)stmt",5,7);
newtype("stmt","dostmtwhile(bool);",7,8);
newtype("stmt","break;",2,9);
newtype("stmt","block",1,10);
newtype("bool","expr<expr",3,11);
newtype("bool","expr<=expr",4,12);
newtype("bool","expr>expr",3,13);
newtype("bool","expr>=expr",4,14);
newtype("bool","expr",1,15);
newtype("expr","expr+term",3,16);
newtype("expr","expr-term",3,17);
newtype("expr","term",1,18);
newtype("term","term*term",3,19);
newtype("term","term/term",3,20);
newtype("term","factor",1,21);
newtype("factor","(expr)",3,22);
newtype("factor","id",1,23);
newtype("factor","num",1,24);
newtype("stst","stmt",1,25);
}
(1)初始化一條規則
public static void newtype(String right,String left,int pop,int key)
{
type type=new type();
type.right=right;
type.left=left;
type.pop=pop;
rule.put(key, type);
}
(5)查找非終結符
public static String isFinal(String a)//查找終結符
{
//找出first
StringBuffer a1=new StringBuffer();
boolean flag=false;
String a2=null;
for(int i=0;i<a.length();i++)
{
a1.append(a.charAt(i));
if(Final.contains(a1.toString()))
{
flag=true;
a2=a1.toString();
break;
}
}
if(flag)
return a2;
else
return null;
}
(6)初始化各種符號(部分)
public static void getTag()//初始化各種符號
{
Final.add("{");
Final.add("}");
Final.add("id");
Final.add("=");
Final.add("if");
Final.add("(");
Final.add(")");
Final.add("else");
Final.add("while");
Final.add("do");
}
(7)詞法分析過程
public static void cifa(String s1) throws IOException//開始語法分析
{
stack.push(1);
stack1.push("$");
System.out.println("步驟\t"+"狀態棧\t"+"符號棧\t輸入串\t"+"action\t"+"goto\t");
int step=1;
int top=1;
while(s1!=null)
{
if(isFinal(s1)!=null)//查找action表
{
Map<String,String> map1=new HashMap<>();
map1=Maction.get(top);//gai
if(map1!=null)
{
if(map1.containsKey(isFinal(s1)))//終結符action
{
String s2=map1.get(isFinal(s1));//操作
if(s2.charAt(0)=='r')//規約
{
String s3=s2.substring(1);
int i=Integer.parseInt(s3);//按照第幾條規則規約
type type=new type();
type=rule.get(i);
int popnum=type.pop;//要彈出幾條
print(step,stack.toString(),stack1.toString(),s1,"按照"+type.right+"->"+type.left+"的規則進行規約",0);
for(int flag=0;flag<popnum;flag++)
{
stack.pop();//狀態棧彈出
stack1.pop();//符號棧彈出
}
top=stack.peek();//頂端
stack1.push(type.right);
Map<String,String> map2=new HashMap<>();
map2=Mgoto.get(top);//非終結符用goto
if(map2.containsKey(type.right))
{
stack.push(Integer.parseInt(map2.get(type.right)));
top=Integer.parseInt(map2.get(type.right));
}
else
{
System.out.println("error sen!");
p.write(("\r\n語法錯誤!\r\n當前狀態 :"+top+"\r\n無法解析符號 :"+s1).getBytes());
break;
}
}
else if(s2.charAt(0)=='s')//移入
{
String s3=s2.substring(1);
print(step, stack.toString(), stack1.toString(),s1, s2, 1);
stack.push(Integer.parseInt(s3));
top=Integer.parseInt(s3);//現在那個狀態
stack1.push(isFinal(s1));
s1=s1.substring(isFinal(s1).length());
}
else if(s2.equals("acc"))
{
//歸約成功
print(step, stack.toString(),stack1.toString(), s1, s2, 1);
p.write(("success!").getBytes());
System.out.println("\r\nsuccess!");
break;
}
}
else
{
//沒有這個操作,出錯
System.out.println("error");
p.write(("\r\n語法錯誤!\r\n當前狀態 : "+top+"\r\n無法解析符號 :"+s1+"\r\n").getBytes());
break;
}
}
else
{
p.write(("\r\n語法錯誤!\r\n當前狀態 : "+top+"\r\n無法解析符號 :"+s1+"\r\n").getBytes());
break;
}
}
else if (isNFinal(s1)!=null)//查找goto表
{
Map<String,String> map2=new HashMap<>();
map2=Mgoto.get(top);//非終結符用goto
if(map2!=null)
{
if(map2.containsKey(isNFinal(s1)))//非終結符有跳轉
{
String s2=map2.get(isNFinal(s1));//跳轉到哪
print(step,stack.toString(),stack1.toString(),s1,s2,0);
top=Integer.parseInt(s2);
stack.push(Integer.parseInt(s2));
s1=s1.substring(isNFinal(s1).length());
}
else
{
p.write(("\r\n語法錯誤!\r\n當前狀態 :"+top+"\r\n無法解析符號 :"+s1+"\r\n").getBytes());
System.out.println("error sentence!");
break;
}
}
else
{
p.write(("\r\n語法錯誤!\r\n當前狀態 :"+top+"\r\n無法解析符號 :"+s1+"\r\n").getBytes());
break;
}
}
else
{
System.out.println("error!can not identify the word!");
p.write(("\r\n語法錯誤!\r\n當前狀態 :"+top+"\r\n無法解析符號 :"+s1+"\r\n").getBytes());
break;
}
step+=1;//步驟加一
}
}
(8)main函數
public static void main(String []arg) throws IOException
{
getActionMap();
getGotoMap();
newrule();
getTag();
p=new FileOutputStream(new File("C:\\Users\\Mac\\Desktop\\result.txt"));
String str=input.getinput();
p.write(("分析代碼\r\n"+str+"\r\n\r\n").getBytes());
cifa(str);
p.close();
}
函數調用圖
測試數據
(1)
- 輸入
- 輸出
(2) - 輸入
+輸出
(3) - 輸入
- 輸出
(4)錯誤 - 輸入
+輸出
錯誤原因:將變量賦值給常量
總結
在這次實驗之前,我只知道LR(1)求向前看符的方法,但是並不知道為什么要用first求。經過這次實驗,我才知道規約的話要在下一個輸入為規約后式子的規約符號下一個符號的first集中才規約,這樣可以縮小規約范圍,及時排錯。
這次實驗最大的困難就是遞歸求LR(1)表,但是我不太會遞歸就只能手動畫LR(1)表了,也算為理論考試做准備。然后就出來了58個狀態,最大的問題就是容易忘記在狀態中加入新的規則,這一點在理論考試的時候要多加注意。