0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

自頂向下的語(yǔ)法分析器—采用遞歸下降方法

冬至子 ? 來(lái)源:趙同學(xué)的代碼時(shí)間 ? 作者:Jun. ? 2023-05-23 11:24 ? 次閱讀

在之前已經(jīng)通過(guò)手寫(xiě)的方式實(shí)現(xiàn)了一個(gè)詞法分析器,現(xiàn)在,我將利用之前手寫(xiě)的詞法分析器,使用遞歸下降的方式,實(shí)現(xiàn)一個(gè)簡(jiǎn)單的語(yǔ)法分析器。

首先看看實(shí)驗(yàn)要求:

遞歸下降法編寫(xiě)一個(gè)語(yǔ)法分析程序,使之與詞法分析器結(jié)合,能夠根據(jù)語(yǔ)言的上下文無(wú)關(guān)文法,識(shí)別輸入的單詞序列是否文法的句子。

為減輕實(shí)驗(yàn)編程負(fù)擔(dān),這里只要求實(shí)現(xiàn)部分產(chǎn)生式,文法的開(kāi)始符號(hào)為 program

圖片

第一步,消除左遞歸。觀察上面各個(gè)產(chǎn)生式,出現(xiàn)左遞歸的地方有expr和term。

圖片

針對(duì)以上式子,首先提取左因子:

圖片

消除左遞歸:

圖片

觀察到 在if語(yǔ)句處也存在左公因子,一并提取,最終文法定義為:

圖片

定義好文法后,考慮詞法分析與語(yǔ)法分析的接口,由于采用遞歸下降的分析過(guò)程中,存在對(duì)token的回溯操作,因此,每次逐個(gè)獲取詞法單元并不容易實(shí)現(xiàn),考慮將token對(duì)應(yīng)的標(biāo)志和內(nèi)容使用pair的數(shù)組保存下來(lái)。

pair<int, string> tokens[10000];
int cursor_;
int tokens_size_;

并使用cursor_記錄當(dāng)前準(zhǔn)備分析的位置,通過(guò)移動(dòng)cursor_實(shí)現(xiàn)對(duì)token串的回溯過(guò)程。然后根據(jù)每一條語(yǔ)句,寫(xiě)出對(duì)應(yīng)的梯度下降遞歸函數(shù),就可以完成本實(shí)驗(yàn),使用yylex自動(dòng)生成的方法也類(lèi)似,只需要維護(hù)tokens數(shù)組即可。

// 工具函數(shù):獲得一個(gè)向前看,但并不移動(dòng)指針
pair<int, string> get_ahead(){
    return tokens[cursor_];
}


// 工具函數(shù):嘗試匹配一個(gè)類(lèi)型,匹配返回真
bool match(int type){
    if(cursor_ >= tokens_size_) return false;
    if(tokens[cursor_].first == type)
    {
        cursor_ ++;
        return true;
    }
    return false;
}


// 工具函數(shù):嘗試匹配一個(gè)類(lèi)型和字符串,都匹配返回真
bool match(int type, string target){
    if(tokens[cursor_].first == type && tokens[cursor_].second == target)
    {
        cursor_ ++;
        return true;
    }
    return false;
}


bool factor(){
    cout << "Try to match factor" << endl;
    if(match(IDENTITY)) return true;
    if(match(DIGIT)) return true;
    return match(SYMBOL, "(") && expr() && match(SYMBOL, ")");
}


bool B3(){
    cout << "Try to match B3" << endl;
    return match(SYMBOL, "%") && factor();
}


bool B2(){
    cout << "Try to match B2" << endl;
    return match(SYMBOL, "/") && factor();
}


bool B1(){
    cout << "Try to match B1" << endl;
    return match(SYMBOL, "*") && factor();
}


bool B(){
    cout << "Try to match B" << endl;
    int backup_cursor = cursor_;
    if(B1()) return true;
    cursor_ = backup_cursor;
    if(B2()) return true;
    cursor_ = backup_cursor;
    if(B3()) return true;
    cursor_ = backup_cursor;
    return false;
}


bool D(){
    cout << "Try to match D" << endl;
    pair<int, string> ahead = get_ahead();
    if(ahead.first == SYMBOL && (ahead.second == "*" || ahead.second == "/" || ahead.second == "%")) return B() && D();
    return true;
}


bool term(){
    cout << "Try to match term" << endl;
    return factor() && D();
}


bool A2(){
    cout << "Try to match A2" << endl;
    return match(SYMBOL, "-") && term();
}


bool A1(){
    cout << "Try to match A1" << endl;
    return match(SYMBOL, "+") && term();
}


bool A(){
    cout << "Try to match A" << endl;
    int backup_cursor = cursor_;
    if(A1()) return true;
    cursor_ = backup_cursor;
    if(A2()) return true;
    cursor_ = backup_cursor;
    return false;
}


bool C(){
    cout << "Try to match C" << endl;
    pair<int, string> ahead = get_ahead();
    if(ahead.first == SYMBOL && (ahead.second == "+" || ahead.second == "-")) return A() && C();
    return true;
}


bool expr(){
    cout << "Try to match expr" << endl;
    return term() && C();
}


bool F(){
    cout << "Try to match F" << endl;
    pair<int, string> ahead = get_ahead();
    if(ahead.first == RELOP) return match(RELOP) && expr();
    return true;
}


bool bool_(){
    cout << "Try to match bool" << endl;
    return expr() && F();
}


bool E(){
    cout << "Try to match E" << endl;
    pair<int, string> ahead = get_ahead();
    if(ahead.first == KEYWORD && ahead.second == "else") return match(KEYWORD, "else") && stmt();
    return true;
}


bool stmt6(){
    cout << "Try to match stmt choice 6" << endl;
    return block();
}


bool stmt5(){
    cout << "Try to match stmt choice 5" << endl;
    return match(KEYWORD, "break") && match(SYMBOL, ";");
}


bool stmt4(){
    cout << "Try to match stmt choice 4" << endl;
    return match(KEYWORD, "do") && stmt() && match(KEYWORD, "while") && match(SYMBOL, "(") && bool_() && match(SYMBOL, ")") && match(SYMBOL, ";");
}


bool stmt3(){
    cout << "Try to match stmt choice 3" << endl;
    return match(KEYWORD, "while") && match(SYMBOL, "(") && bool_() && match(SYMBOL, ")") && stmt();
}


bool stmt2(){
    cout << "Try to match stmt choice 2" << endl;
    return match(KEYWORD, "if") && match(SYMBOL, "(") && bool_() && match(SYMBOL, ")") && stmt() && E();
}


bool stmt1(){
    cout << "Try to match stmt choice 1" << endl;
    return match(IDENTITY) && match(SYMBOL, "=") && expr() && match(SYMBOL, ";");
}


bool stmt(){
    cout << "Try to match stmt" << endl;
    int backup_cursor = cursor_; // 指針的回溯
    if(stmt1()) return true;
    cursor_ = backup_cursor;
    if(stmt2()) return true;
    cursor_ = backup_cursor;
    if(stmt3()) return true;
    cursor_ = backup_cursor;
    if(stmt4()) return true;
    cursor_ = backup_cursor;
    if(stmt5()) return true;
    cursor_ = backup_cursor;
    if(stmt6()) return true;
    cursor_ = backup_cursor;
    return false;
}


bool stmts(){
    cout << "Try to match stmts" << endl;
    pair<int, string> ahead = get_ahead();
    if(ahead.first == SYMBOL && ahead.second == "}") return true;
    else return stmt() && stmts();
}


bool block(){
    cout << "Try to match block" << endl;
    return match(SYMBOL, "{") && stmts() && match(SYMBOL, "}");
}


bool program(){
    cout << "Try to match program" << endl;
    return block();
}

由于已經(jīng)高度模塊化,main函數(shù)很簡(jiǎn)單:

int main()
{
    if(!DFA()) return 0; // 詞法分析
    tokens_size_ = cursor_; 
    cursor_ = 0; // 初始化指針
    if(program()) cout << "ACCEPT !" << endl;
    else cout << "ERROR !" << endl;
}

測(cè)試:

使用實(shí)驗(yàn)指導(dǎo)代碼測(cè)試:

input:
{
i = 2;
   sum = 0;
     while (i <=100)   {
          sum = sum + i;
       i = i + 2;
          if(i%5==0) break;
     }
}


output:
SYMBOL : {
i = 2;
IDENTITY : i
SYMBOL : =
DIGIT : 2
SYMBOL : ;
   sum = 0;
IDENTITY : sum
SYMBOL : =
DIGIT : 0
SYMBOL : ;
KEYWORD : while
SYMBOL : (
IDENTITY : i
RELOP : <=
DIGIT : 100
SYMBOL : )
SYMBOL : {
IDENTITY : sum
SYMBOL : =
IDENTITY : sum
SYMBOL : +
IDENTITY : i
SYMBOL : ;
IDENTITY : i
SYMBOL : =
IDENTITY : i
SYMBOL : +
DIGIT : 2
SYMBOL : ;
KEYWORD : if
SYMBOL : (
IDENTITY : i
SYMBOL : %
DIGIT : 5
RELOP : ==
DIGIT : 0
SYMBOL : )
KEYWORD : break
SYMBOL : ;
SYMBOL : }
SYMBOL : }
Try to match program
Try to match block
Try to match stmts
Try to match stmt
Try to match stmt choice 1
Try to match expr
Try to match term
Try to match factor
Try to match D
Try to match C
Try to match stmts
Try to match stmt
Try to match stmt choice 1
Try to match expr
Try to match term
Try to match factor
Try to match D
Try to match C
Try to match stmts
Try to match stmt
Try to match stmt choice 1
Try to match stmt choice 2
Try to match stmt choice 3
Try to match bool
Try to match expr
Try to match term
Try to match factor
Try to match D
Try to match C
Try to match F
Try to match expr
Try to match term
Try to match factor
Try to match D
Try to match C
Try to match stmt
Try to match stmt choice 1
Try to match stmt choice 2
Try to match stmt choice 3
Try to match stmt choice 4
Try to match stmt choice 5
Try to match stmt choice 6
Try to match block
Try to match stmts
Try to match stmt
Try to match stmt choice 1
Try to match expr
Try to match term
Try to match factor
Try to match D
Try to match C
Try to match A
Try to match A1
Try to match term
Try to match factor
Try to match D
Try to match C
Try to match stmts
Try to match stmt
Try to match stmt choice 1
Try to match expr
Try to match term
Try to match factor
Try to match D
Try to match C
Try to match A
Try to match A1
Try to match term
Try to match factor
Try to match D
Try to match E
Try to match stmts
Try to match stmts
ACCEPT !

最后發(fā)現(xiàn)梯度遞歸函數(shù)已經(jīng)成功地接受了我們的輸入。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 分析器
    +關(guān)注

    關(guān)注

    0

    文章

    92

    瀏覽量

    12455
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    [4.1.1]--向下語(yǔ)法分析及其面臨的問(wèn)題

    編譯原理
    jf_60701476
    發(fā)布于 :2022年12月27日 11:27:59

    基于向下技術(shù)的工程機(jī)械Digital Prototyping設(shè)計(jì)方法及應(yīng)用

    【作者】:劉雪冬【來(lái)源】:《華南理工大學(xué)》2009年【摘要】:向下的設(shè)計(jì)方法及裝配建模技術(shù)是在消費(fèi)品行業(yè)應(yīng)用比較成熟的一種設(shè)計(jì)方法和理論
    發(fā)表于 04-24 09:20

    postgreSQL命令的詞法分析語(yǔ)法分析

    PostgreSQL查詢(xún)SQL的語(yǔ)法分析(1)——詞法分析
    發(fā)表于 05-16 16:33

    如何實(shí)現(xiàn)擴(kuò)頻通信調(diào)制向下的設(shè)計(jì)?

    如何實(shí)現(xiàn)擴(kuò)頻通信調(diào)制向下的設(shè)計(jì)?如何實(shí)現(xiàn)擴(kuò)頻通信調(diào)制的仿真測(cè)試?
    發(fā)表于 04-29 06:46

    一個(gè)高效的語(yǔ)法分析器生成工具

    VPGE(Visual Parser Generation Environment)是一個(gè)可視化語(yǔ)法分析器集成開(kāi)發(fā)環(huán)境,除了具有良好的界面和強(qiáng)大的調(diào)試功能,其LALR(1)分析器的生成速度達(dá)到并超過(guò)公認(rèn)的分析器生成速度最快
    發(fā)表于 08-29 10:04 ?16次下載

    YACC在ATLAS語(yǔ)言語(yǔ)法分析中的沖突消解研究

    對(duì)使用YACC工具進(jìn)行ATLAS語(yǔ)言語(yǔ)法分析過(guò)程中出現(xiàn)的大量沖突進(jìn)行了詳細(xì)的分類(lèi)討論與研究,給出了實(shí)現(xiàn)過(guò)程中出現(xiàn)的主要沖突類(lèi)型及相應(yīng)解決方案:文法符號(hào)的不斷自身循環(huán)產(chǎn)生
    發(fā)表于 09-08 15:30 ?0次下載

    網(wǎng)絡(luò)分析器,網(wǎng)絡(luò)分析器原理是什么?

    網(wǎng)絡(luò)分析器,網(wǎng)絡(luò)分析器原理是什么? 網(wǎng)絡(luò)分析器   具有發(fā)現(xiàn)并解決各種故障特性的硬件或軟件設(shè)備
    發(fā)表于 03-22 11:25 ?1033次閱讀

    編譯原理實(shí)踐環(huán)節(jié)模擬試題

    1.為以下文法構(gòu)造遞歸下降語(yǔ)法分析程序,并能對(duì)輸入串進(jìn)行語(yǔ)法分析。 S aBc|bAB A aAb|b B b 2.試寫(xiě)出簡(jiǎn)單的詞法分析程序
    發(fā)表于 04-11 22:19 ?24次下載

    借助Lex和Yacc進(jìn)行詞法語(yǔ)法分析

    實(shí)驗(yàn)?zāi)康模?1.通過(guò)對(duì)實(shí)驗(yàn)型程序設(shè)計(jì)語(yǔ)言C1的定義,掌握程序設(shè)計(jì)語(yǔ)言的基本語(yǔ)法和語(yǔ)義; 2.使用Lex及Yacc實(shí)現(xiàn)詞法分析語(yǔ)法分析;
    發(fā)表于 04-18 23:04 ?30次下載

    交換機(jī)端口分析器

    本文將重點(diǎn)介紹“交換端口分析器(SPAN)”的工作原理及配置方法。
    發(fā)表于 02-03 14:09 ?969次閱讀

    通過(guò)模塊之間的調(diào)用實(shí)現(xiàn)向下的設(shè)計(jì)

    通過(guò)模塊之間的調(diào)用實(shí)現(xiàn)向下的設(shè)計(jì)目的:學(xué)習(xí)狀態(tài)機(jī)的嵌套使用實(shí)現(xiàn)層次化、結(jié)構(gòu)化設(shè)計(jì)。
    發(fā)表于 02-11 05:53 ?2403次閱讀
    通過(guò)模塊之間的調(diào)用實(shí)現(xiàn)<b class='flag-5'>自</b><b class='flag-5'>頂</b><b class='flag-5'>向下</b>的設(shè)計(jì)

    EDA設(shè)計(jì)一般采用向下的模塊化設(shè)計(jì)方法

    三方面的電子設(shè)計(jì)工作,即集成電路設(shè)計(jì)、電子電路設(shè)計(jì)以及PCB設(shè)計(jì)??傊?,EDA技術(shù)的基本特征是采用具有系統(tǒng)仿真和綜合能力的高級(jí)語(yǔ)言描述。它一般采用
    發(fā)表于 01-21 16:50 ?8847次閱讀
    EDA設(shè)計(jì)一般<b class='flag-5'>采用</b><b class='flag-5'>自</b><b class='flag-5'>頂</b><b class='flag-5'>向下</b>的模塊化設(shè)計(jì)<b class='flag-5'>方法</b>

    開(kāi)源L2C編譯前端語(yǔ)法分析器及驗(yàn)證過(guò)程

    Jourdan等在其2012年發(fā)表的論文“ Validating Lr(1) Parsers”中提出了一種形式化驗(yàn)證語(yǔ)法分析器方法,并將其成功地應(yīng)用于 Compcert編譯(2.3以上版本
    發(fā)表于 05-19 10:55 ?5次下載

    計(jì)算機(jī)網(wǎng)絡(luò):向下

    本文檔包含Jim Kurose和Keith Ross編寫(xiě)的《計(jì)算機(jī)網(wǎng)絡(luò):向下方法(第7版)》復(fù)習(xí)題和問(wèn)題的參考答案。這些答案只對(duì)指導(dǎo)老師有效。請(qǐng)不要復(fù)制或者分發(fā)給其他人(即使是其他指導(dǎo)老師)。請(qǐng)
    發(fā)表于 03-13 14:23 ?0次下載

    eda向下的設(shè)計(jì)方法 eda自頂向下設(shè)計(jì)優(yōu)點(diǎn)

    EDA(Electronic Design Automation,電子設(shè)計(jì)自動(dòng)化)向下的設(shè)計(jì)方法是一種常見(jiàn)的電子電路設(shè)計(jì)方法。該
    發(fā)表于 04-10 16:49 ?3480次閱讀