編譯原理實驗1:構造詞法分析器及解釋器

来源:https://www.cnblogs.com/zjwoo/archive/2020/04/25/12775104.html
-Advertisement-
Play Games

詞法分析器 一、 目標和要求 首先本次實驗分為三個小題分別為:C語言詞法分析器、四則運算文法、解釋器。因此以下一 ~ 九部分是C語言詞法分析器的實驗內容,十 ~ 十三部分是四則運算及其解釋器的實驗內容。 1.第一小題: 明確目標: 按照已經掌握的C語言的詞法規範,編寫能夠按照C語言規範識別每個詞法符 ...


詞法分析器

一、 目標和要求

首先本次實驗分為三個小題分別為:C語言詞法分析器、四則運算文法、解釋器。因此以下一 ~ 九部分是C語言詞法分析器的實驗內容,十 ~ 十三部分是四則運算及其解釋器的實驗內容。

 

1.第一小題:

明確目標:

按照已經掌握的C語言的詞法規範,編寫能夠按照C語言規範識別每個詞法符號的分析器。從一個文本文件(典型地,就是C語言的源程式文件)中讀入字元流,經過識別之後逐個輸出詞法符號(只需原樣輸出識別到的詞法符號,無需考慮語法屬性)。

幾個要求:

(1)假定該語言有:

預處理指令:以#開頭。

字元常量:單撇號作為界標;

字元串常量:雙撇號作為界標;

數值型常量:數字開頭,如016、0x8b、1.0f、3L都是,這裡只選擇整數;

標識符:非數字開頭的下劃線字母數字串(包括保留字);

運算符:有單字元的,也有多字元的如++、+=、&&=、sizeof等。

分隔符:逗號,分號,括弧,花括弧,冒號(定義標號時使用);

(2)至少要有一個空白符將相鄰的標識符、數值型常量和關鍵字隔開,但不能用空白符將符號中的相鄰字元分開。

(3)註釋是以符號/*開始,並以*/的第一次出現作為結束,或者以//開頭。

(4)需要在輸出結果前說明該符號的詞法屬性。例如:預定義:#include<stdio.h>

 

2.第二小題:

明確目標:

構造一個能夠實現四則運算的文法。

幾個要求:

對文法的符號、規則、特性以及語言給出準確解釋。

 

3.第三小題:

明確目標:

根據四則運算文法構造編譯器。

幾個要求:

能夠按照文法規則,逐行解釋執行輸入內容。

 

 

二、數據模型

單詞符號

類型編碼

助記符

單詞符號

類型編碼

助記符

標識符

1

$SYMBOL

字元常量

2

$CHARA

字元串常量

3

$STRING

數值型常量

4

$NUMER

 

Int

5

$INT

If

6

$IF

Else

7

$ELSE

While

8

$WHILE

For

9

$FOR

Read

10

$READ

Write

11

$WRITE

+

12

$ADD

-

13

$SUB

*

14

$MUL

/

15

$DIV

<

16

$L

++

17

$INC

--

18

$DEC

<=

19

$LE

>

20

$G

>=

21

$GE

!=

22

#NE

==

23

#E

=

24

#ASSIGN

(

25

#LPAR

)

26

#RPAR

,

27

#COM

;

28

#SEM

表1 數據模型圖

 

三、描述輸出

對於程式段 for(k=0; k<=100; ++k)將產生下麵的結果。

步驟

輸出

含義

1

9,     ’for’

標識符for

2

25,     ‘(‘

分隔符(

3

1,    ’k’

標識符k

4

24,     ’=’

運算符=

5

4,     ‘0’

數值型常量

6

28,     ’;’

分隔符;

7

1,     ’k’

標識符k

8

19,     ’<=’

運算符<=

9

4,     ‘100’

數值型常量

10

28,     ’;’

分隔符;

11

17,     ’++’

運算符++

12

1,      ’k’

標識符k

13

26,     ‘)’

分隔符)

表2 輸出描述

 

 

 

 

四、演算法

 

圖1 狀態轉換圖

 

 

 

五、演算法細化

 

圖2 狀態轉換圖細化

六、流程圖、偽代碼

 

圖3 流程圖

七、編程

1、預定義與頭文件

#include<stdio.h>

#include<ctype.h> //包含isspace函數 ,isalnum()是否字母或數字,isalpha()是否字母

//預處理是以>作為結束,字元是以'作為結束,字元串是以"作為結束,

//數值型常量只能根據當前字元的下一個字元是不是數字判斷是否結束,標識符也是根據下一個字元判斷。所以要記錄下一個字元。 

 

2、全局變數

int current_ch, next_ch,k=0;

int flag = 0;

char word[1024];

FILE *fp;

 

3、函數原型

//讀下一個字元

void read_next_char();

//預處理指令:

void get_preprocessing_line();

//提取字元型常量:\'

void get_char_const();

//提取字元串常量

void get_string_const();

//提取數值型常量,讀取文本只有整數:

void get_num_const();

//提取標識符:

void get_identifier();

//提取運算符

void get_operator();

//提取分隔符

void get_seperator();

//詞法分析部分的基本流程

int lexical_analyzer(FILE * input_file)

int main()

{

fp = fopen("test.txt","r");

if(fp == NULL)

{

printf("無法找到文件\n");

return 0;

}

current_ch = fgetc(fp);//當前字元

lexical_analyzer(fp);

return 0;

}

完整代碼見附錄。

 

八、測試

集成測試:測試過程中出現了很多問題,最主要的一個就是對next_ch定義不明確。比如預處理指令判斷是否current_ch=’>’結束,字元是判斷current_ch是否等於單引號和雙引號結束。而數值型常量不同,必須判斷next_ch是否為數字,標識符也是同樣的判斷next_ch。所以我添加了一個flag進行判斷。

 

 

圖4 測試文件

 

圖5 輸出示例

九、總結

整體來說,本次實驗的難度不大,實驗過程並不涉及語法分析,只是對C語言簡單的詞法分析。題目一給出自然想到的就是使用多個判斷條件,判斷出究竟是保留字、變數、常量、運算符、分隔符、頭文件和預定義還是註釋。其次就是對輸入流字元的處理,我們使用一個字元型數組word來保存。使用flag來判斷current_ch是字元型還是數值型。我們使用C語言面向過程的編程方式進行模塊化的編程,結構比較清晰。

經過參與這次詞法分析的實驗,我們對C語言的詞法有了更進一步的理解。以及對編程語言的此法構成有了進一步的瞭解。知道了一些編譯器的基本工作方式。相信在接下來的學習中我們會對電腦編譯原理有更深層次的理解。

 

四則運算

十、四則運算文法

四則運算屬於2型文法,所以制定以下文法規則:

運算符→+-*/

整數→[1-9][0-9]* | 0

浮點數→整數.[0-9]*

數→整數 | 浮點數

算式→數 | 算式 運算符 數

 

用相應符號表示:

算式:S,運算符:P,整數:N,浮點數:F,數:D

P→+-*/

N→[1-9][0-9]* | 0

F→N.[0-9]*

D→N | F

S→D | S P D

 

十一、解釋器

解釋器的製作需要藉助lex和Yacc兩個工具來完成,在DEV C++環境下運行。

(1)先使用lex和Yacc的編程規則編寫好兩個文件:

 

圖6 生成詞法和語法解釋器的兩個文件

(2)然後進入兩個文件的所在目錄命令行視窗按順序輸入以下命令:

$env:Path += ";C:\usr\local\wbin"

flex gr1.txt

bison -y -d gr2.txt

 

(3)最後將生成的兩個.c文件添加到同一項目中運行即可。

 

詞法文件編碼:

 

圖7詞法分析

 

文法文件編碼:

 

圖8 語法分析

 

十二、測試

四則運算可以正常執行:

 

圖9 運算測試

以下為一些極端測試:

 

 

 

 

 

圖10 極端測試

 

根據以上一些極端測試,可以看出,大部分輸入錯誤該程式都可以進行適當的處理。一些特出錯誤,程式識別不出來的數據,會直接結束運行。對於大數相乘程式會用浮點數的形式給出結果,並不能算出精確值,最大可精確到小數點後5位。所以可以看出功能還是比較局限的。

 

十三、總結

在這次試驗中我感到lex和Yacc這兩個工具的強大,它們可以幫助我們快速生成非常全面解釋器,使我們的工作轉移到詞法和語法分析上。相信在接下來的學習中我們會對電腦編譯原理有更深層次的理解。

 

附錄

#include<stdio.h>

#include<ctype.h> //包含isspace函數 ,isalnum()是否字母或數字,isalpha()是否字母

//預處理是以>作為結束,字元是以'作為結束,字元串是以"作為結束,

//數值型常量只能根據當前字元的下一個字元是不是數字判斷是否結束,標識符也是根據下一個字元判斷。所以要記錄下一個字元。

int current_ch, next_ch,k=0;

int flag = 0;

char word[1024];

FILE *fp;

 

 

//讀下一個字元

void read_next_char()

{

current_ch = fgetc(fp);

}

 

//預處理指令:

void get_preprocessing_line()

{

k = 1;

while(1)

{

word[k++]=fgetc(fp);

if(word[k-1] == '>')  //碰到>跳出迴圈

{

printf("預處理指令:");

break;

 }

}

}

 

//提取字元型常量:\'

void get_char_const()

{

k = 1;

while(1)

{

word[k++]=fgetc(fp);

if(word[k-1] == '\'')  //碰到'跳出迴圈

{

printf("字元型常量:");

break;

 }

}

}

 

//提取字元串常量

void get_string_const()

{

k = 1;

while(1)

{

word[k++]=fgetc(fp);

if(word[k-1] == '\"')  //碰到"跳出迴圈

{

printf("字元串常量:");

break;

 }

}

}

 

 

//提取數值型常量,讀取文本只有整數:

void get_num_const()

{

k = 1;

while(1)

{

word[k++]=fgetc(fp);

if(word[k-1] - '0' < 0 || word[k-1] - '0' > 9)  //碰到非數字跳出迴圈 ,要把char型word轉為int型數字

{

next_ch = word[k-1]; //記錄最後的字元

word[k-1] = '\0';

printf("數值型常量:");

break;

 }

}

}

 

//提取標識符:

void get_identifier()

{

k = 1;

while(1)

{

word[k++]=fgetc(fp);

if(!(isalnum(word[k-1]) || word[k-1] == '_'))  //碰到非字母或數字或下劃線跳出迴圈

{

next_ch = word[k-1]; //記錄最後的字元

word[k-1] = '\0';

printf("標識符:");

break;

}

}

}

 

//提取運算符

void get_operator()

{

word[1] = next_ch;

printf("多字元運算符:");

}

 

//提取分隔符

void get_seperator()

{

printf("分隔符:");

}

 

//詞法分析部分的基本流程

int lexical_analyzer(FILE * input_file)

{

int i;

int ch;

while(isspace(current_ch)) read_next_char();//碰到空格,當前word結束

while((ch = current_ch) != EOF)

{

word[0] = ch;

if(ch == '#') get_preprocessing_line();//預處理指令:

else if(ch== '\'') get_char_const();//提取字元常量:\'

else if(ch=='"') get_string_const();//提取字元串常量

else if(ch>='0'&&ch<='9') {get_num_const();current_ch = next_ch;flag = 1;}//提取數值型常量:

else if(ch=='_' || isalpha(ch)) {get_identifier();current_ch = next_ch;flag = 1;}//提取標識符下劃線或字母開頭:isalpha()判斷字元變數c是否為字母

else if(ch == (int)'+' || ch == (int)'-' || ch == (int)'*' || ch == (int)'/' || ch == (int)'<' || ch == (int)'>' || ch == (int)'!' || ch == (int)'=') //提取運算符

{

next_ch = fgetc(fp);

if(next_ch != '+' && next_ch != '-' && next_ch != '=') //如果下一個字元不是+-=,那麼是單字元運算符,否則是多字元運算符

{printf("單字元運算符") ;current_ch = next_ch;flag = 1;}

else

{get_operator();}

}

else if(ch == (int)'(' || ch == (int)')' || ch == (int)',' || ch == (int)';' || ch == (int)'{' || ch == (int)'}')    get_seperator();//提取分隔符

else { printf("Unknown syntactics:%c(0X%X)\n",ch,ch); return 1; }

printf("%s\n", word);

for(i = 0;i < k;i++) //word置空

{

word[i] = '\0';

}

do{

if(flag==0)

read_next_char();

flag=0;

}while(isspace(current_ch));

}

}

 

int main()

{

fp = fopen("test.txt","r");

if(fp == NULL)

{

printf("無法找到文件\n");

return 0;

}

current_ch = fgetc(fp);//當前字元

lexical_analyzer(fp);

return 0;

}

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Python爬蟲為什麼受歡迎 如果你仔細觀察,就不難發現,懂爬蟲、學習爬蟲的人越來越多,一方面,互聯網可以獲取的數據越來越多,另一方面,像 Python這樣的編程語言提供越來越多的優秀工具,讓爬蟲變得簡單、容易上手。 利用爬蟲我們可以獲取大量的價值數據,從而獲得感性認識中不能得到的信息,這裡要註意: ...
  • python入門介紹 一、編程語言的分類 分類: 機器語言: 用二進位代碼0和1描述的指令稱為機器指令,由於電腦內部是基於二進位指令工作的,所以機器語言是直接控制電腦硬體 彙編語言: 彙編語言的實質和機器語言是相同的,都是直接對硬體操作,只不過指令採用了英文縮寫的標識符,更容易識別和記憶 高級語 ...
  • 編程語言及電腦介紹 一、編程語言是什麼 語言其實就是人與人之間溝通的介質/工具,比如英語、法語等 而編程語言則是人與電腦之間溝通的介質,常見的編程語言有python、java、php、.net等 二、為什麼要編程 編程就是人把自己想電腦做的事,也就是自己的思維邏輯,用編程語言表達出來 編程的目 ...
  • 1 package com.yhqtv.demo01Exception; 2 /* 3 * 一、異常體繫結構 4 *java.lang.Throwable 5 * java.lang.Error:一般不編寫針對性的代碼進行處理。 6 * java.lang.Exception:可以進行異常的處理 7 ...
  • 前言 文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理。 爬蟲的網站:萬邦國際集團。其成立於2010年,總部位於河南省鄭州市,以“立足三農、保障民生、服務全國”為宗旨,業務涵蓋綜合性農產品冷鏈物流、高效生態農業開發、生鮮連鎖超市、跨境 ...
  • 前言 由於AQS的源碼太過凝練,而且有很多分支比如取消排隊、等待條件等,如果把所有的分支在一篇文章的寫完可能會看懵,所以這篇文章主要是從正常流程先走一遍,重點不在取消排隊等分支,之後會專門寫一篇取消排隊和等待條件的分支邏輯。讀源碼千萬別在每個代碼分支中來回游走,先按一個正常的分支把流程看明白,之後再 ...
  • 案例故事: Android App或者系統測試過程中,涉及需要斷網異常測試(無網路情況下,App或系統是否提示正常,運行正常), 聯網測試(網路恢復的情況下,App或系統是否提示正常,運行正常), 目前基本上設備都具備wifi,4G兩種網路, 需要考慮兩種網路全部斷開, 或者兩種網路全部連上,並需要 ...
  • 說明 這裡基於 php7.2.5 進行測試,php7 之後內部結構變化應該不是太大,但與 php5.X 有差別。 函數分類 用戶自定義函數 say(); function say() { echo "周傑倫"; } php hello.php 周傑倫 cli 模式下我們執行這個代碼之後就會輸出函數調 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...