【编译原理】自顶向下的递归下降语法分析器 解析
任務(wù):給定一個算術(shù)表達式的無關(guān)文法,實現(xiàn)一個語法分析器
分析:
根據(jù)一個上下文無關(guān)語法生成一個遞歸下降的語法分析器需要注意幾個方面(思路、步驟):
1.觀察給定語法,如果遇到左遞歸,則需要改寫語法來消除左遞歸
2.根據(jù)給定的語法,生成相應(yīng)符號的First集和Fllow集
3.依照First集和Fllow集實現(xiàn)語法分析器的代碼
一、消除左遞歸:
原本為左遞歸的語法會使得語法分析器無限循環(huán),無法與給定的輸入串進行匹配。我們可以使用左遞歸將其改寫為右遞歸,該方法是通用的(引入一個新的非終結(jié)符進行過渡):
這時候我們將原來的左遞歸的語法改寫成用右遞歸表示的語法:
二、進行完左遞歸后,我們要求出新生成語法中相應(yīng)符號的First集和Fllow集:
該步的目的是讓語法分析器產(chǎn)生“預(yù)測”的能力,可以根據(jù)當(dāng)前輸入中給定的字符來當(dāng)前語法要進行哪個對應(yīng)的語法規(guī)則轉(zhuǎn)換,從而避免因選擇了錯誤的語法分支而需要進行回溯所帶來的性能影響。
接下來講講什么是符號的First集和Fllow集。
First集是對所有的符號來說的(包括終結(jié)符和非終結(jié)符),它所表示的含義是從該符號出發(fā),能夠推導(dǎo)出的所有句子(只包含終結(jié)符)中,句子開頭可能的非終結(jié)符有哪些,這些非終結(jié)符組成該符號的First集。
而Fllow集則是對所有的終結(jié)符(準(zhǔn)確的說,是對所有FIrst集中含ε的非終結(jié)符來說)來說的,因為其First集中存在ε(即從一個非終結(jié)符出發(fā)可以不推出任何語句),所以該非終結(jié)符根據(jù)相應(yīng)的語法規(guī)則推導(dǎo)得出的句子中,該句子的第一個符號不一定存在于它的First集合中(此時該非終結(jié)符選擇了能推導(dǎo)出ε所在分支的語法,記作語法X),那么要想讓他具備“預(yù)測”的能力,我們就需要知道在使用了語法X(能推導(dǎo)出ε的語法規(guī)則)后,其后面所跟的非終結(jié)符可能有哪些,這些非終結(jié)符就是所求的Fllow集。
First集求解:
先給出求解First集合的偽代碼,再講解一下求解First集合的步驟:
1.對于終結(jié)符的First集合,我們可以直接得到,就是該終結(jié)符本身
2.而對于非終結(jié)符的First集合,我們需要遍歷所有的推導(dǎo),對于一條推導(dǎo)A -> B1 B2 ... Bn來說,若B1的First集合不含ε,則First(A)= First(B1),否則First(A)還包含F(xiàn)irst(B2),以此類推,直到當(dāng)前所出Bi的First集合不含ε為止。這里需要注意一點只有當(dāng)Bn的First集也含ε時,才將ε加入到A的First集合中
3.直到所有的First集不再變化時(不動點)
通過以上求解,我們可以得到我們所求的語法的First集合,如下表示(將終結(jié)符和非終結(jié)符區(qū)分開來表示):
Fllow集的求解:
同樣的,先給出Fllow集合的偽代碼,再對步驟進行講解:
1.對于所有的推導(dǎo)語句A -> B1 B2 ... Bn 來說,首先進行初始化,先將文件終結(jié)符EOF加入到所有處于推導(dǎo)語句左側(cè)的非終結(jié)符的Fllow集合中 ,表示所有的A語句結(jié)束后,后面都可以跟文件終結(jié)符EOF
2.對于所有的推導(dǎo)語句A -> B1 B2 ... Bn 來說,我們從后往前求出每個非終結(jié)符Bi的Fllow集。對于Bn來說,F(xiàn)llow(Bn) = Follow(A)。
對于Bn-1來說,如果First(Bn)含ε時,F(xiàn)llow(Bn-1) = First(Bn)∪Fllow(A);如果First(Bn)不含ε時,F(xiàn)llow(Bn-1) = First(Bn),以此類推
3.直到所有的Fllow集不再變化時(不動點)
通過以上求解我們得到所有的非終結(jié)符的Fllow集如下表示:
三、編寫遞歸下降語法分析器
對于每一條語法的推導(dǎo)都有一個函數(shù)實現(xiàn)
有較為詳細(xì)的錯誤提示(如:第幾個字符出現(xiàn)錯誤,希望出現(xiàn)XX字符,結(jié)果出現(xiàn)了XX字符)
特別注意:什么時候調(diào)用其他的解析函數(shù),什么時候報錯,什么時候利用First集合和Fllow集合進行預(yù)測,以及左右括號匹配的地方
先給出偽代碼實現(xiàn):
再給出我的代碼實現(xiàn):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *str;
size_t ind;
void Expr();
void Eprime();
void Term();
void Tprime();
void Factor();
void Parse(char *s);
void error(char *want, char got) {
//report syntax error
printf("Compling this expression:
%s
", str);
printf ("Syntax error at position: %d
"
" expecting: %s
"
" but got : %c
",
ind, want, got);
//attempt error recovery or exit
//exit(0);
exit(0);
return;
}
void Expr() {
/* Expr -> Term Expr' */
Term();
Eprime();
return;
}
void Eprime() {
/* Expr' -> + Term Expr' */
/* Expr' -> - Term Expr' */
if (str[ind] == '+' || str[ind] == '-') { //先看First集
ind++;
Term();
} else if (str[ind] == '') { //含ε再看Fllow集
return;
} else { //沒有得到期望的字符
error("\0 or + or - or ) ", str[ind]);
}
return;
}
void Term() {
/* Term -> Factor Term' */
Factor();
Tprime();
return;
}
void Tprime() {
/* Term' -> * Factor Term' */
/* Term' -> / Factor Term' */
if (str[ind] == '*' || str[ind] == '/') {
ind++;
Factor();
} else if (str[ind] == '' || str[ind] == '+' || str[ind] == '-') {
return;
} else {
error("\0 or * or / or ) or + or -", str[ind]);
}
return;
}
void isNumber() {
while (str[ind] >= '0' && str[ind] <= '9') {
ind++;
}
}
void isName() {
while ((str[ind] >= 'a' && str[ind] <= 'z') ||
(str[ind] >= 'A' && str[ind] <= 'Z')) {
ind++;
}
}
void Factor() {
/* Factor -> num */
/* Factor -> name */
if (str[ind] >= '0' && str[ind] <= '9') {
isNumber();
return;
} else if ((str[ind] >= 'a' && str[ind] <= 'z') ||
(str[ind] >= 'A' && str[ind] <= 'Z')) {
isName();
return;
}
/* Factor -> ( Expr ) */
if (str[ind] == '(') {
ind++;
Expr();
if (str[ind] != ')') {
error("number or name or )", str[ind]);
}
ind++;
} else {
error("number or name or (", str[ind]);
}
}
void Parse(char *s) {
/* Goal -> Expr */
ind = 0;
str = s;
Expr();
if (str[ind] != '') {
error("\0", str[ind]);
}
return;
}
int main( ) {
char *e;
/* Test */
//e = "(2)";
//Parse(e);
//e = "(3+4*5))";
//Parse(e);
//e = "(8-2)*3";
//Parse(e);
//e = "(8-2)/3";
//Parse(e);
}
遞歸下降語法分析器
如果您對文章中的內(nèi)容有什么疑問,或者有錯誤的地方,可以在評論區(qū)中提出。
如果您覺得這篇文章幫到了您,或者覺得這篇文章寫的不錯,希望可以點個贊。
如果您覺得這篇文章在某些方面(包括但不限于寫法,結(jié)構(gòu),組織等方面)可以改進,希望可以在評論區(qū)中提出的自己的建議。
多謝您的參與!
文章所引用的參考資料:
《編譯器設(shè)計(第二版)》
網(wǎng)易云課堂《編譯原理》(華保健)
總結(jié)
以上是生活随笔為你收集整理的【编译原理】自顶向下的递归下降语法分析器 解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ElasticSearch插件安装
- 下一篇: 怎么创建具有真实纹理的CG场景岩石?