在上一題篇中講到了詞法分析的過程,需要的可以點擊查看 詞法分析——TEST編譯器(1)
1 TEST語法規則
語法分析是根據TEST語法規則來實現的。語法規則表明了每一個句子長什麼樣子,組成一個句子的單詞都是以怎樣的順序排列的,例如<fun_declaration>表示一個函數,它由 function ID ’(‘ ‘ )’ < function_body> 構成,即函數名 + ‘(’ + ‘)’ + 函數體
(1). < program> ::={fun_declaration }<main_declaration>
(2). <fun_declaration>::= function ID’(‘ ‘ )’< function_body>
(3). <main_declaration>::=main’(‘ ‘ )’ < function_body>
(4). <function_body>::= ‘{’<declaration_list><statement_list> ‘}’
(5). <declaration_list>::=<declaration_list><declaration_stat> |ε <declaration_list>::={<declaration_stat>}
(6). <declaration_stat>::=int ID;
(7). <statement_list>::=<statement_list>| ε <statement_list>::={}
(8). < statement>::=<if_stat>|<while_stat>|<for_stat>|<read_stat> |<write_stat>|<compound_stat> |<expression_stat> | < call _stat>
(9). <if_stat>::= if ‘(‘’)’ [else < statement >]
(10). <while_stat>::= while ‘(‘’)’ < statement >
(11). <for_stat>::= for’(‘;;’)’
(12). <write_stat>::=write ;
(13). <read_stat>::=read ID;
(14). <compound_stat>::=’{‘<statement_list>’}’
(15). <expression_stat>::=< expression >;|;
(16). < call _stat>::= call ID‘(’ ‘)’
(17). < expression >::= ID=<bool_expr>|<bool_expr>
(18). < bool_expr>::=< additive_expr> | < additive_expr >(>|<|>=|<=|== | !=) < additive_expr > < bool_expr>::=< additive_expr >{(>|<|>=|<=|==|!=)< additive_expr>}
(19). < additive_expr>::={(+|-)< term >}
(20). < term >::={(*| /)< factor >}
(21). < factor >::=‘(’ < additive_expr >‘)’|ID|NUM
2 語法分析
2.1 功能
- 根據語法規則將詞法分析得到的二元組實現“組詞成句”,構造語法樹
- 指出語法錯誤
2.2 錯誤類型
- 缺少 ‘{’ 或 ‘}’
- 缺少 ‘(’ 或 ‘)’
- 缺少 ‘;’
- 根據語法規則可知,在定義變量時、表達式中需要指明具體的標識符,如果缺少標識符就屬於語法錯誤
- 由於表達式由操作數和運算符組成,操作數可能是常數或變量,如果缺少操作數就屬於語法錯誤
2.3 設計思路
- 思路就是一個個讀入二元組信息,使用遞歸下降的方式,從第一個單詞開始進入詞法分析函數,根據語法規則從左到右分析每一個單詞是否符合規則。當讀到的單詞是一個非終結符,就遞歸調用對應的函數,當對應的函數處理完後會返回一個flag,標記着該函數是否存在語法錯誤,如果沒有就繼續往下檢查剩餘單詞。如果在某一個規則中出現了語法錯誤就根據flag退出整個遞歸過程輸出語法錯誤。
- 由於畫出樹形的語法樹比較困難,則選擇行縮進的方式表達語法樹。要按順序輸出這些內容就要在語法分析的過程中把它們記錄下來,最後再按一定的順序輸出。可以創建一個結構體,用來保存當前數據的內容和屬於它的子內容,當讀取到一種一個數據時就將它的值賦值給這個結構體,把它的每一個子內容都添加到其中。最後只需要從根結點開始不斷遞歸遍歷子內容就可以了。
爲了體現出層次,可以設置一個循環次數的參數,每一行都先循環x次輸出x個空格,然後再輸出應該輸出的內容。由於是遞歸輸出,那麼每遞歸到一個子結點時x的值就要比父結點的x值多1,即多輸出一個空格,從而就能體現出層次感 - 當使用fprintf輸出的內容是字符串,但結果是亂碼,原因是數據指向一個指針,要使用c_str()返回一個指向正規C字符串的指針常量。
- 實現帶參數函數,原本的函數定義是檢查完 ’(‘ 之後就直接檢查 ’)’ ,如果要加參數的話就要,在括號之間進行檢查。由於可能存在多個參數的情況,需要用循環判斷每一個參數。如果是多個參數的話,每個參數之間用逗號隔開,那麼逗號就是存在多個參數的標誌,判斷完一個參數之後沒有之後逗號了就退出循環,如果有逗號就繼續重複判斷。但是在這個地方可能會出錯,如果出現逗號但之後沒有新的參數了應該屬於缺少參數錯誤。
3 完整代碼
#include<bits/stdc++.h>
using namespace std ;
struct tree {
string data ;
vector<tree*> son ;
tree* addSon(string str) {
tree* newSon = new tree() ;
newSon->data = str ;
son.push_back(newSon) ;
return newSon ;
}
} *root;
FILE *inputFile ; //輸入文件
FILE *outputFile ; //輸出文件
char inputAddress[20] ; //輸入文件地址
char outputAddress[20] ; //輸出文件地址
char type[20] ; //單詞類型
char value[40] ; //單詞值
int line = 0 ; //當前讀到第幾行
//存在函數之間相互調用,要先聲明函數
int parseAlanalysis() ;
int program() ;
int fun_declaration(tree* father) ;
int main_declaration(tree* father) ;
int function_body(tree* father) ;
int declaration_list(tree* father) ;
int declaration_stat(tree* father) ;
int statement_list(tree* father) ;
int statement(tree* father) ;
int if_stat(tree* father) ;
int while_stat(tree* father) ;
int for_stat(tree* father) ;
int read_stat(tree* father) ;
int write_stat(tree* father) ;
int compound_stat(tree* father) ;
int call_stat(tree* father) ;
int expression_stat(tree* father) ;
int expression(tree* father) ;
int bool_expr(tree* father) ;
int additive_expr(tree* father) ;
int term(tree* father) ;
int factor(tree* father) ;
int do_while_stat(tree* father) ;
void getNext() ;
//將當前數據存入語法樹中
void add(tree* p) {
p->addSon(value) ;
}
//輸出語法樹
void printTree(tree* p, int x) {
for(int i=0;i<x;i++) //每次輸出之前都要先輸出一定的空格實現深度
fprintf(outputFile, "` ") ;
fprintf(outputFile,"%s\n",p->data.c_str()) ;
int num = p->son.size() ;
if(num == 0)
return ;
for(int i=0; i<num; i++) //輸出子內容
printTree(p->son[i],x+1); //深度加一
return ;
}
//讀取下一行內容
void getNext() {
fscanf(inputFile, "%s%s\n", type, value) ;
printf("第%d行 %s %s\n", line, type, value) ;
line++ ;
}
//判斷條件
int judge(tree* father) {
int flag = 0 ;
//檢查 (
getNext() ;
if(strcmp("(", type) != 0)
return 4 ;
add(father) ;
//檢查 expr
getNext() ;
flag = expression(father) ; //判斷條件
if(flag > 0)
return flag ;
//可能存在多重判斷語句
while(strcmp("&&", type)==0 || strcmp("||", type)==0) {
add(father) ;
//檢查 expr
getNext() ;
flag = expression(father) ; //判斷條件
}
//檢查 )
if(strcmp(")", type) != 0)
return 5 ;
add(father) ;
return flag ;
}
//運算因子
int factor(tree* father) { // < factor >::=‘(’ < additive_expr >‘)’|ID|NUM
tree* current = father->addSon("<factor>") ;
int flag = 0 ;
//括號有優先級,需要先處理其中的算術表達式
if(strcmp("(", type) == 0) {
add(current) ;
getNext() ;
flag = additive_expr(current) ;
if(flag > 0)
return flag ;
if(strcmp(")", type) != 0)
return 5 ;
add(current) ;
getNext() ;
}
else { //因子還可能是標識符或者常數,都是最小單位了,沒問題就直接返回
if(strcmp("ID", type)==0 || strcmp("NUM", type)==0) {
add(current) ;
getNext() ;
return flag ;
}
else
return 7 ;
}
return flag ;
}
//運算項
int term(tree* father) { //< term >::=<factor>{(*| /)< factor >}
tree* current = father->addSon("<term>") ;
int flag = 0 ;
//檢查 term
flag = factor(current) ;
if(flag > 0)
return flag ;
//之後可能有多個運算過程所以要用循環不能用if
while(strcmp("*", type)==0 || strcmp("/", type)==0) {
add(current) ;
//檢查 term
getNext() ;
flag = factor(current) ;
if(flag > 0)
return flag ;
}
return flag ;
}
//算數表達式
int additive_expr(tree* father) { // < additive_expr>::=<term>{(+|-)< term >}
tree* current = father->addSon("<additive_expr>") ;
int flag = 0 ;
//檢查 term
flag = term(current) ;
if(flag > 0)
return flag ;
//之後可能有多個運算過程所以要用循環不能用if
while(strcmp("+", type)==0 || strcmp("-", type)==0) {
add(current) ;
//檢查 term
getNext() ;
flag = term(current) ;
if(flag > 0)
return flag ;
}
return flag ;
}
//布爾表達式
int bool_expr(tree* father) { //<bool_expr>::=<additive_expr>|<additive_expr>(>|<|>=|<=|==|!=)<additive_expr>
// <bool_expr>::=<additive_expr>{(>|<|>=|<=|==|!=)<additive_expr>}
tree* current = father->addSon("<bool_expr>") ;
int flag = 0 ;
//檢查 additive_expr
flag = additive_expr(current) ;
if(flag > 0)
return flag ;
//之後可能有各種關係運算符,有就繼續判斷,沒有就結束
if(strcmp(">", type)==0 || strcmp("<", type)==0 || strcmp(">=", type)==0 ||
strcmp("<=", type)==0 || strcmp("==", type)==0 || strcmp("!=", type)==0) {
add(current) ;
//檢查 additive_expr
getNext() ;
flag = additive_expr(current) ;
if(flag > 0)
return flag ;
}
return flag ;
}
//表達式
int expression(tree* father) { // < expression >::= ID=<bool_expr>|<bool_expr>
tree* current = father->addSon("<expression>") ;
int flag = 0 ;
int filePlace ; //記錄當前文件位置
char t1[40], t2[40] ; //臨時存取數據
//此處進來的數據是ID
if(strcmp("ID", type) == 0) { //< expression >::= ID=<bool_expr>
//add(current) ;
filePlace = ftell(inputFile) ; //進來數據在文件中的數據
//檢查 =
fscanf(inputFile, "%s%s\n", t1, t2) ; //首先把數據存到臨時的數組中
line++ ;
if(strcmp("=", t1) == 0) { //該表達式例如 a=xxxx
printf("第%d行 %s %s\n", line, t1, t2) ;
add(current) ;
current->addSon(t1) ;
//檢查 bool_expr
getNext() ; //如果進入到此處就繼續往下讀取數據就可以了,之後數據還是存在原來的數組中
flag = bool_expr(current) ;
if(flag > 0)
return flag ;
}
else { //該表達式例如 a>=xxxx
line-- ; //多讀取的行數要減掉
fseek(inputFile, filePlace, 0) ; //讀取數據返回到 = 前的標識符,即剛進來的ID的位置
flag = bool_expr(current) ;
if(flag > 0)
return flag ;
}
}
else //< expression >::= <bool_expr>
flag = bool_expr(current) ;
return flag ;
}
//表達式序列
int expression_stat(tree* father) { //<expression_stat>::=< expression >;|;
tree* current = father->addSon("<expression_stat>") ;
//add(current) ;
int flag = 0 ;
//檢查第一個是 ;
if(strcmp(";", type) == 0) {
add(current) ;
getNext() ;
return flag ;
}
//檢查expression
flag = expression(current) ;
if(flag > 0)
return flag ;
//檢查末尾的 ;
if(strcmp(";", type) == 0) {
add(current) ;
getNext() ;
return 0 ;
}
else //缺少 ;
return 3 ;
}
//調用函數語句
int call_stat(tree* father) { // < call _stat>::= call ID‘(’ ‘)’ ;
tree* current = father->addSon("<call _stat>") ;
add(current) ;
//檢查 ID
getNext() ;
if(strcmp("ID", type) != 0)
return 6 ;
add(current) ;
//檢查 (
getNext() ;
if(strcmp("(", type) != 0)
return 4 ;
add(current) ;
getNext() ;
//可能帶有參數
while(strcmp("NUM", type)==0 || strcmp("ID", type)==0) { //call xxx(yyy,....)
add(current) ;
//檢查是否存在多個參數
getNext() ;
if(strcmp(",", type) == 0) { //有逗號說明有多個參數
add(current) ;
getNext() ;
if(strcmp("NUM", type)!=0 && strcmp("ID", type)!=0) //如果逗號之後不是新的參數就出錯
return 10 ;
}
else //沒有參數了就結束
break ;
}
//檢查 )
if(strcmp(")", type) != 0)
return 5 ;
add(current) ;
getNext() ;
//檢查 ;
if(strcmp(";", type) != 0)
return 3 ;
add(current) ;
getNext() ;
return 0 ; //沒有問題就返回0
}
//複合語句
int compound_stat(tree* father) { // <compound_stat>::=’{‘<statement_list>’}‘
tree* current = father->addSon("<compound_stat>") ;
add(current) ;
int flag = 0 ;
getNext() ;
flag = statement_list(current) ;
return flag ; //不管是否正確直接返回, } 在statement_list已經判斷了
}
//write語句
int write_stat(tree* father) { // <write_stat>::=write <expression>;
tree* current = father->addSon("<write_stat>") ;
add(current) ;
int flag = 0 ;
//檢查 expression
getNext() ;
flag = expression(current) ;
if(flag > 0)
return flag ;
//檢查 ;
if(strcmp(";", type) != 0)
return 3 ;
add(current) ;
getNext() ;
return 0 ;
}
//read語句
int read_stat(tree* father) { //<read_stat>::=read ID;
tree* current = father->addSon("<read_stat>") ;
add(current) ;
//檢查 ID
getNext() ;
if(strcmp("ID", type) != 0)
return 6 ;
add(current) ;
//檢查 ;
getNext() ;
if(strcmp(";", type) != 0)
return 3 ;
add(current) ;
getNext() ; //讀取下一個語句的內容
return 0 ;
}
//for語句
int for_stat(tree* father) { // <for_stat>::= for’(‘<expr>;<expr>;<expr>’)’<statement>
tree* current = father->addSon("<for_stat>") ;
add(current) ;
int flag = 0 ;
//檢查 (
getNext() ;
if(strcmp("(", type) != 0)
return 4 ;
add(current) ;
for(int i=0; i<2; i++) {
//檢查 expr
getNext() ;
flag = expression(current) ; //判斷條件
if(flag > 0)
return flag ;
//檢查 ;
if(strcmp(";", type) != 0)
return 3 ;
add(current) ;
}
//檢查 expr
getNext() ;
flag = expression(current) ; //判斷條件
if(flag > 0)
return flag ;
//檢查 )
if(strcmp(")", type) != 0)
return 5 ;
add(current) ;
//檢查 statement
getNext() ;
flag = statement(current) ; // for中的語句
return flag ; //最後一個語句,不管是否正確都直接輸出即可
}
//do_while語句
int do_while_stat(tree* father) { // do ‘{’ < statement > ‘}’ while ‘(‘<expr >’)’ ;
tree* current = father->addSon("<do_while_stat>") ;
add(current) ;
int flag = 0 ;
//檢查 {
getNext() ;
if(strcmp("{", type) != 0)
return 1 ;
//檢查執行語句
flag = statement(current) ; //檢查完執行語句後 } 已經檢查完了
if(flag > 0)
return flag ;
//檢查 while
if(strcmp("while", type) != 0)
return 11 ;
add(current) ;
//檢查判斷語句
flag = judge(current) ;
if(flag > 0)
return flag ;
getNext() ;
return flag ;
}
//while語句
int while_stat(tree* father) { //<while_stat>::= while ‘(‘<expr >’)’ < statement >
tree* current = father->addSon("<while_stat>") ;
add(current) ;
int flag = 0 ;
flag = judge(current) ;
if(flag > 0)
return flag ;
//檢查 statement
getNext() ;
flag = statement(current) ; // while中的語句
return flag ; //最後一個語句,不管是否正確都直接輸出即可
}
//if語句
int if_stat(tree* father) { // <if_stat>::= if ‘(‘<expr>’)’ <statement > [else < statement >]
tree* current = father->addSon("<if_stat>") ;
add(current) ;
int flag = 0 ;
flag = judge(current) ; //判斷條件
if(flag > 0)
return flag ;
//檢查 statement
getNext() ;
flag = statement(current) ; // if中的語句
if(flag > 0)
return flag ;
//檢查 else
if(strcmp("else", type) == 0) { //當前的數據由上一步中獲得了
add(current) ;
//檢查 statement
getNext() ;
flag = statement(current) ; //else中的語句
if(flag > 0)
return flag ;
}
return flag ;
}
//語句內容
int statement(tree* father) {
/*<statement>::=<if_stat>|<while_stat>|<for_stat>|<read_stat>|<write_stat>|
<compound_stat> |<expression_stat> | < call _stat> */
tree* current = father->addSon("<statement>") ;
int flag = 0 ;
//從聲明中結束後得到第一個語句單詞
if(flag==0 && strcmp("if", type)==0)
flag = if_stat(current) ;
if(flag==0 && strcmp("while", type)==0)
flag = while_stat(current) ;
if(flag==0 && strcmp("for", type)==0)
flag = for_stat(current) ;
if(flag==0 && strcmp("read", type)==0)
flag = read_stat(current) ;
if(flag==0 && strcmp("write", type)==0)
flag = write_stat(current) ;
if(flag==0 && strcmp("{", type)==0) //複合語句要從 { 開始
flag = compound_stat(current) ;
if(flag==0 && strcmp("call", type)==0)
flag = call_stat(current) ;
if(flag==0 && (strcmp("(", type)==0 || strcmp(";", type)==0 ||
strcmp("NUM", type)==0) || strcmp("ID", type)==0)
flag = expression_stat(current) ;
if(flag==0 && strcmp("do", type)==0) //do_while語句
flag = do_while_stat(current) ;
return flag ;
}
//語句序列
int statement_list(tree* father) { //<statement_list>::=<statement_list><statement>| ε
//<statement_list>::={<statement>}
tree* current = father->addSon("<statement_list>") ;
int flag = 0 ;
while(strcmp("}", type) != 0) { //到 } 結束
if(feof(inputFile))
return 2 ;
flag = statement(current) ; //查看具體的語句
if(flag > 0)
return flag ;
}
add(current) ;
getNext() ;
return flag ;
}
//聲明內容
int declaration_stat(tree* father) { //<declaration_stat>::=int ID;
tree* current = father->addSon("<declaration_stat>") ;
add(current) ;
//檢查 ID
getNext() ;
if(strcmp("ID", type) != 0) //缺少標識符
return 6 ;
add(current) ;
//檢查 賦值
getNext() ;
if(strcmp("=", type) == 0) { //變量名之後是 = 則需要賦值
add(current) ;
//檢查 賦值
getNext() ;
if(strcmp("NUM", type) != 0) //缺少賦值
return 7 ;
add(current) ;
//檢查 ;
getNext() ;
if(strcmp(";", type) != 0) //缺少 ;
return 3 ;
add(current) ;
}
else { //檢查 ;
if(strcmp(";", type) != 0) //缺少 ;
return 3 ;
add(current) ;
}
return 0 ; //正常就返回0
}
//聲明序列
int declaration_list(tree* father) { //<declaration_list>::=<declaration_list><declaration_stat> |ε
//<declaration_list>::={<declaration_stat>}
tree* current = father->addSon("<declaration_list>") ;
int flag = 0 ;
while(1) {
getNext() ;
if(strcmp("int", type) != 0) //不是int說明已經沒有變量聲明瞭進入語句內容
break ;
flag = declaration_stat(current) ; //具體的聲明內容
if(flag > 0)
return flag ;
}
return flag ;
}
//函數體
int function_body(tree* father) { //<function_body>::= ‘{’<declaration_list><statement_list> ‘}’ (
tree* current = father->addSon("<function_body>") ;
int flag = 0 ;
//檢查 {
getNext() ;
if(strcmp("{", type) != 0) //缺少 {
return 1 ;
add(current) ;
//檢查 declaration_list
flag = declaration_list(current) ; //如果成功了當前的單詞是語句的內容
if(flag > 0)
return flag ;
//檢查 statement_list
flag = statement_list(current) ; //一開始不用讀取新的內容,成功了當前的單詞應該是 }
return flag ;
}
//主函數
int main_declaration(tree* father) { //<main_declaration>::=main’(‘ ‘ )’ < function_body>
tree* current = father->addSon("<main_declaration>") ;
add(current) ;
getNext() ;
//檢查 main
if(strcmp("main", value) != 0) //程序中最後的聲明必須是名字爲 main 的主函數
return 6 ;
add(current) ;
//檢查 (
getNext() ;
if(strcmp("(", type) != 0) //缺少 (
return 4 ;
add(current) ;
//檢查 )
getNext() ;
if(strcmp(")", type) != 0) //缺少 )
return 5 ;
add(current) ;
//檢查 function_body
return function_body(current) ; //檢查函數體
}
//函數聲明
int fun_declaration(tree* father) { // <fun_declaration>::= function ID’(‘ ‘ )’< function_body>
tree* current = father->addSon("<fun_declaration>") ; //當前類型
add(current) ; //當前數據沒問題就將其加入到樹中
//檢查 ID
getNext() ;
if(strcmp("ID", type) != 0) //缺少標識符代表函數名
return 6 ;
add(current) ;
//檢查 (
getNext() ;
if(strcmp("(", type) != 0) //缺少 (
return 4 ;
add(current) ;
//檢查參數
getNext() ;
while(strcmp("int", type) == 0) { //int xxx (,....)
add(current) ;
//檢查 標識符
getNext() ;
if(strcmp("ID", type) != 0)
return 6 ;
add(current) ;
//檢查是否存在多個參數
getNext() ;
if(strcmp(",", type) == 0) { //有逗號說明有多個參數
add(current) ;
getNext() ;
if(strcmp("int", type) != 0) //如果逗號之後不是新的參數就出錯
return 10 ;
}
else //沒有參數了就結束
break ;
}
//檢查 )
if(strcmp(")", type) != 0) //缺少 )
return 5 ;
add(current) ;
//檢查 function_body
return function_body(current) ; //檢查函數體
}
//程序
int program() { //<program> ::={fun_declaration }<main_declaration>
int flag = 0 ;
root = new tree() ;
root->data = "<program>" ;
//檢查 全局變量,可能有也可能沒有
flag = declaration_list(root) ; //如果成功了當前的單詞是語句的內容
if(flag > 0)
return flag ;
//檢查 fun_declaration
while(strcmp("function", type) == 0) { //函數聲明,可能有也可能沒有
flag = fun_declaration(root) ; //檢查函數聲明
if(flag > 0)
return flag ; //若函數有問題就返回錯誤類型
}
if(strcmp("int", type) == 0) { //主函數
//檢查 main_declaration
flag = main_declaration(root) ; //檢查主函數內容
if(flag > 0)
return flag ;
}
else
return 6 ;
return flag ;
}
//語法分析
int parseAlanalysis() {
//初始化,得到輸入輸出文件
printf("請輸入 輸入文件地址:") ;
scanf("%s", inputAddress) ;
if((inputFile=fopen(inputAddress, "r")) == NULL) //讀取輸入文件
return 8 ; //讀取文件出錯就返回對應錯誤類型
printf("請輸入 輸出文件地址:") ;
scanf("%s", outputAddress) ;
if((outputFile=fopen(outputAddress, "w")) == NULL) //讀取輸出文件
return 9 ; //讀取文件出錯就返回對應錯誤類型
printf("\n") ;
return program() ; //文件讀取成功就看看裏面的內容
}
int main() {
int flag = 0 ;
flag = parseAlanalysis() ; //遞歸下降語法分析
//處理結果
printf("\n\n=============語法分析結果=====================\n\n") ;
if(flag == 0) {
printf("語法分析成功\n") ;
printTree(root, 0) ;
}
else {
printf("語法分析失敗\n") ;
switch (flag) { //選則具體的錯誤類型
case 1 : printf("第%d行缺少 { \n", line-1) ; break ;
case 2 : printf("第%d行缺少 } \n", line-1) ; break ;
case 3 : printf("第%d行缺少 ; \n", line-1) ; break ;
case 4 : printf("第%d行缺少 ( \n", line-1) ; break ;
case 5 : printf("第%d行缺少 ) \n", line-1) ; break ;
case 6 : printf("第%d行缺少標識符\n", line-1) ; break ;
case 7 : printf("第%d行缺少操作數\n", line-1) ; break ;
case 8 : printf("無法打開輸入文件%s\n", inputAddress); break ;
case 9 : printf("無法打開輸出文件%s\n", outputAddress); break ;
case 10 : printf("第%d行缺少參數\n", line-1); break ;
case 11 : printf("第%d行缺少保留字\n", line-1); break ;
}
}
return 0 ;
}
4 總結
在做語法分析之前,老師給我們發了一個文件,裏面是語法分析的代碼,讓我們嘗試着先讀一讀這個代碼。我讀完之後大概知道了遞歸下降實現語法分析是什麼樣子,之後自己打代碼就有了一個大概的方向。雖然說這樣有點劇透的感覺,但我覺得讀這個代碼的關鍵不是要我們一模一樣地模仿,而是提供一個思路,並且閱讀他人代碼我認爲是一件很有意義的事情,能夠增強閱讀、理解他人代碼的能力。其實我認爲在自己獨立打完一份代碼之後也應該去網上找找他人類似的代碼,與其進行比較,找出他人代碼的亮點,看看有哪些東西是值得自己學習的。每次自己花了很大勁做出來一個很一般的代碼,再看看其他人很好很巧妙的方法就會恍然大悟,這樣做最終的理解也會更深刻