當前位置:首頁 » 編程軟體 » 編譯原理語法分析報告

編譯原理語法分析報告

發布時間: 2024-10-27 01:17:02

① 詞法分析和語法分析區別

詞法分析和語法分析是編譯原理中的兩個重要概念,它們在編譯過程中扮演不同的角色。
1. 詞法分析(Lexical Analysis):詞法分析是編譯器的第一階段,也稱為掃描(Scanning)或詞法掃描(Tokenization)。它的主要任務是將源代碼轉化為一個個的詞法單元(Token)。詞法單元是具有獨立含義的字元序列,比如關鍵字、標識符、數字常量、運算符等。詞法分析器根據事先定義好的詞法規則(正則表達式或有限狀態自動機)對源代碼進行掃描和識別,生成詞法單元流作為後續語法分析的輸入。
2. 語法分析(Syntax Analysis):語法分析是編譯器的第二階段,也稱為解析(Parsing)。它的主要任務是根據語法規則分析詞法單元流,確定語法結構,並構建對應的語法樹(Parse Tree)或抽象語法樹(Abstract Syntax Tree)。語法規則通常使用上下文無關文法的巴科斯-諾爾範式(BNF)。語法分析器通過遞歸下降、LR分析等演算法,從詞法單元流中識別語法結構,並進行相應的語法規約和移進操作,最終得到語法樹或者抽象語法樹。
總結而言,詞法分析關注於單詞的識別和分類,將源代碼切分為有意義的詞法單元;而語法分析則關注於將詞法單元通過語法規則組織起來,構建出語法結構。兩者相互配合,是編譯過程中的重要組成部分,並且是後續語義分析和代碼生成的基礎。

② 編譯原理語法分析編程

#include <iostream>
#include <string>
#include <fstream>
#include <queue>
#include <string.h>
#include <stdio.h>

using namespace std;

enum Datatype { RESERVE_WORD=1,IDENTIFIER=2,DIGIT=3,OPERATOR=4,SEPRATOR=5 };

struct OutputStruct
{
public:
Datatype type;
string value;
};

string operate[]={"sin","cos","pow"};
string KeyWord[]={"main","int","if","char","cout"};
const int MAX_SIZE=255;
char BUFF[MAX_SIZE]; //buffer to contain a char line.
ifstream inFile;
ofstream outFileStream;
queue<OutputStruct> tt;

bool IsKeyWord(string& cs)
{
for(int i=0;i<5;++i)
if(cs==KeyWord[i])
return true; //Exist
return false;
}

void ReadLineAndAnalyze()
{
int strSize=0;
int i;
int errFlag=0;
char ch;
string outStructStr,str;
struct OutputStruct outStruct;
{
i=0;
inFile.getline(BUFF,MAX_SIZE,'\n');
strSize=inFile.gcount();
cout<<BUFF;
do{
str="";
do{
ch=BUFF[i];
i++;
}while(ch==' '||ch==' '||ch=='\n');
switch(ch)
{

case '+':
case '-':
case '*':
case '/':
outStruct.type=OPERATOR;
outStruct.value=ch;
break;
case '=':
case '>':
case '<':
outStructStr=ch;
if(BUFF[i]=='=')
{
outStruct.type=OPERATOR;
outStructStr+=BUFF[i];
outStruct.value=outStructStr;
i++;
}
else
{
outStruct.type=OPERATOR;
outStruct.value=ch;
};
break;

case ',':
case ';':
case '{':
case '}':
case '(':
case ')':
case '[':
case ']':
case '\"':
outStruct.type=SEPRATOR;
outStruct.value=ch;
break;

case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
outStructStr+=ch;
while(BUFF[i]>='0'&&BUFF[i]<='9'||BUFF[i]=='.')
{
outStructStr+=BUFF[i];
i++;
}//while
outStruct.type=DIGIT;
outStruct.value=outStructStr;
break;

default:
if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z')
{
outStructStr+=ch;
while(BUFF[i]>='a'&&BUFF[i]<='z'||BUFF[i]>='A'&&BUFF[i]<='Z')
{
outStructStr+=BUFF[i];
i++;
}//while
if(IsKeyWord(outStructStr))
{
outStruct.type=RESERVE_WORD;
outStruct.value=outStructStr;
}
else
{
outStruct.type=IDENTIFIER;
outStruct.value=outStructStr;
}
break;
}
else
errFlag=1;
}//switch;
if(!errFlag)
tt.push(outStruct);
errFlag=0;
outStructStr="";
}while(i<strSize-1);

}//while(i<MAX_SIZE&&!inFile.eof());//do_while
return;
}

float F();
float T();
float E();
float S();

float F()
{
float ret;
if((tt.front().type==IDENTIFIER)||(tt.front().type==DIGIT))
{
ret=atof(tt.front().value.c_str());
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
return ret;
}
if(tt.front().value=="(")
{
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
ret=E();
if(tt.front().value==")")
{
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
return ret;
}
else
{
cout<<"\b ----ERROR! "<<tt.front().value<<" 缺少右括弧"<<endl;
cout<<"Press \"enter\" to modify the data file!";
getchar();
system("notepad data.txt");
exit(0);
}
}
else
{
cout<<"\b ----ERROR! "<<tt.front().value<<" 缺少因子"<<endl;
cout<<"Press \"enter\" to modify the data file!";
getchar();
system("notepad data.txt");
exit(0);
}
}
float T()
{
float i,j;
i=F();
if(tt.front().value=="*")
{
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
j=T();
return i*j;
}
else if(tt.front().value=="/")
{
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
j=T();
if(abs(j)<0.0000001)
{
cout<<"\b ----ERROR! 除數為零!"<<endl;
cout<<"Press \"enter\" to modify the data file!";
getchar();
system("notepad data.txt");
exit(0);
}
return i/j;
}
return i;
}

float E()
{
float i,j;
i=T();
if(tt.front().value=="+")
{
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
j=E();
i=i+j;
}
else if(tt.front().value=="-")
{
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
j=E();
i=i-j;
}
if(tt.front().value==";"||tt.front().type==OPERATOR||tt.front().value==")")
return i;
else
{
cout<<"\b ----ERROR! "<<tt.front().value<<" 缺少運算符"<<endl;
cout<<"Press \"enter\" to modify the data file!";
getchar();
system("notepad data.txt");
exit (0);
}
}

float S()
{
float i;
i=E();
if(tt.front().value==";")
{
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
return i;
}
cout<<"\b ----ERROR! "<<tt.front().value<<" 缺少左括弧"<<endl;
cout<<"Press \"enter\" to modify the data file!";
getchar();
system("notepad data.txt");
exit(0);
}

void GrammaAnalize()
{
float i;
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
i=S();
cout<<"\b="<<i<<endl;
}

int main()
{
inFile.open("data.txt");
if(!inFile)
{
cout<<"打開源文件失敗!";
return 1;
}
while(!inFile.eof())
{
ReadLineAndAnalyze();
GrammaAnalize();
}
inFile.close();
return 0;
}

③ 編譯原理筆記9:語法分析樹、語法樹、二義性的消除

語法分析樹和語法樹不是一種東西 。習慣上,我們把前者叫做「具體語法樹」,其能夠體現推導的過程;後者叫做「抽象語法樹」,其不體現過程,只關心最後的結果。

語法分析樹是語言推導過程的圖形化表示方法。這種表示方法反映了語言的實質以及語言的推導過程。

定義:對於 CFG G 的句型,分析樹被定義為具有下述性質的一棵樹:

推導,有最左推導和最右推導,這兩種推導方式在推導過程中的分析樹可能不同,但因最終得到的句子是相同的,所以最終的分析樹是一樣的。

分析樹能反映句型的推導過程,也能反映句型的結構。然而實際上,我們往往不關心推導的過程,而只關心推導的結果。因此,我們要對 分析樹 進行改造,得到 語法樹 。語法樹中全是終結符,沒有非終結符。而且語法樹中沒有括弧

定義:

說白了,語法樹這玩意,就一句話: 葉子全是操作數,內部全是操作符 ,樹里沒有非終結符也不能有括弧。

語法樹要表達的東西,是操作符(運算)作用於操作數(運算對象)

舉倆例子吧:

【例】: -(id+id) 的語法樹:

【例】:-id+id 的語法樹:

顯然,我們從上面這兩個語法樹中,直接就能觀察出來它們的運算順序。

【例】:句型 if C then s1 else s2

二義性問題:一個句子可能對應多於一棵語法樹。

【例】: 設文法 G: E → E+E | E*E | (E) | -E | id

則,句子 id+id*id、id+id+id 可能的分析樹有:

在該例中,雖然 id+id+id 的 「+」 的結合性無論左右都不會影響結果。但萬一,萬一「+」的含義變成了「減法」,那麼左結合和右結合就會引起很大的問題了。

我們在這里講的「二義性」的「義」並非語義——我們現在在學習的內容是「語法分析器」,尚未到需要研究語言背後含義的階段。

我們現在講的「二義性」指的是一個句子對應多種分析樹。

二義性的體現,是文法對同一句子有不止一棵分析樹。這種問題由【句子產生過程中的某些推導有多於一種選擇】引起。懸空 else 問題就可以很好地體現這種【超過一種選擇】帶來的二義性問題,示例如下。

看下面這么個例子。。

(其實,我感覺這個其實比較像是「說話大喘氣」帶來的理解歧義問題。。。)上面的產生式中並沒體現出來該咋算分一塊,所以兩種完全不同的句子結構都是合法的。

二義性問題是有救的,大概有以下這三種辦法:

這些辦法的核心,其實都是將優先順序和結合性說明白。

核心:把優先順序和結合性說明白

既然要說明白,那就不能讓一個非終結符可以直接在當次推導中能推出會帶來優先順序和結合性歧義的東西。(對分析樹的一個內部節點,不會有出現在其下面的分支是相同的非終結符的情況。如果有得選,那就有得歧義了。沒得選才能確定地一路走到黑)

改寫為非二義文法的二義文法大概有下面這幾個特點:

改寫的關鍵步驟:

【例】改寫下面的二義文法為非二義文法。圖右側是要達成的優先順序和結合性

改寫的核心其實就兩句話:

所以能夠得到非終結符與運算的對應關系(因為不同的運算有不同的優先順序,我們想要引入多個優先順序就要引入多個新的非終結符。這樣每個非終結符就可以負責一個優先順序的運算符號,也就是說新的非終結符是與運算有關系的了。因此這里搞出來了「對應關系」四個字)如下:

優先順序由低到高分別是 +、 、-,而距離開始符號越近,優先順序越低。因此在這里的排序也可以+ -順序。每個符號對應一層的非終結符。根據所需要的結合性,則可確定是左遞歸還是右遞歸,以確定新的產生式長什麼樣子

【例】:規定優先順序和結合性,寫出改寫的非二義文法

我們已經掌握了一種叫做【改寫】的工具,能讓我們消除二義性。接下來我們就要用這個工具來嘗試搞搞懸空 else 問題!

懸空 else 問題出現的原因是 then 數量多於 else,讓 else 有多個可以結合的 then。在二義文法中,由於選哪兩個 then、else 配對都可以,故會引起出現二義的情況。在這里,我們規定 else 右結合,即與左邊最靠近的 then 結合。

為改寫此文法,可以將 S 分為完全匹配(MS)和不完全匹配(UMS)兩類。在 MS 中體現 then、else 個數相等即匹配且右結合;在UMS 中 then、else 不匹配,體現 else 右結合。

【例】:用改寫後的文法寫一個條件語句

經過檢查,無法再根據文法寫出其他分析樹,故已經消除了二義性

雖然二義文法會導致二義性,但是其並非一無是處。其有兩個顯著的優點:

在 Yacc 中,我們可以直接指定優先順序、結合性而無需自己重寫文法。

left 表示左結合,right 表示右結合。越往下的算符優先順序越高。

嗯就這么簡單。。。

我們其實可以把語言本身定義成沒有優先順序和結合性的。。然後所有的優先、結合都交由括弧進行控制,哪個先算就加括弧。把一個過程的結束用明確的標志標記出來。

比如在 Ada 中:

在 Pascal 中,給表達式加括弧:

④ 急(高懸賞 幫個忙) 求編譯原理課程設計---c語言實現c-的語法分析,在線等

新建一個文本文檔在你工程目錄下,名字起為"輸入.txt",裡面的內容可以為
begin a:=1+7*(6+3);b:=1end#

輸出是在"輸出.txt"中查看,以下為輸出情況:

詞法分析結果如下:
(1, begin)
(10, a)
(18, :=)
(11, 1)
(13, +)
(11, 7)
(15, *)
(27, ()
(11, 6)
(13, +)
(11, 3)
(28, ))
(26, ;)
(10, b)
(18, :=)
(11, 1)
(6, end)
(0, #)
語法分析結果如下:(以四元式形式輸出)
( +, 6, 3, t1)

( *, 7, t1, t2)

( +, 1, t2, t3)

( =, t3, __, a)

( =, 1, __, b)

//提供一個編譯原理的語義分析程序 你可以直接復制 用TC進行調試
#include "stdio.h"
#include "string.h"
#include <malloc.h>
#include <conio.h>
#include "stdlib.h"

char prog[100],token[8],ch;
char *rwtab[6]={"begin","if","then","while","do","end"};
int syn,p,m,n,sum,q;
int kk;
//四元式表的結構如下:
struct
{
char result1[8];
char ag11[8];
char op1[8];
char ag21[8];
}quad[20];

char *factor();
char *expression();
int yucu();
char *term();
int statement();
int lrparser();
char *newtemp();
void scaner();
void emit(char *result,char *ag1,char *op,char *ag2);

void main()
{
FILE *fp1,*fp2;

if((fp1=fopen("輸入.txt","rt"))==NULL)
{
printf("Cannot open 輸入.txt\n");
getch();
exit(1);
}
if((fp2=fopen("輸出.txt","wt+"))==NULL)
{
printf("Cannot create 輸出.txt FILE.strike any key exit");
getch();
exit(1);
}

int j;
q=p=kk=0;
p=0;
//printf("Please Input a String(end with '#'):\n");
while(ch!='#')
{
ch = fgetc(fp1);
if(ch == EOF)
{
printf("文件為空,請檢查後再嘗試!");
return ;
}

prog[p++]=ch;
}
if(prog[p]=='#')
{
printf("輸入的待分析的串不是以'#'結尾,請修改之後再嘗試!\n");
return;
}
p=0;
char buffer1[200] = {0};
sprintf(buffer1,"詞法分析結果如下:\n");
fputs(buffer1,fp2);
//printf("詞法分析結果如下:\n");
do
{
scaner();
switch(syn)
{
case 11:
//printf("(%d,%d)\n",syn,sum);
sprintf(buffer1,"(%d, %d) \n",syn,sum);
fputs(buffer1,fp2);
break;
default:
//printf("(%d,%s)\n",syn,token);
sprintf(buffer1,"(%d, %s)\n",syn,token);
fputs(buffer1,fp2);
break;
}
}while(syn!=0);
printf("\n");

p=0;
char buffer[200]={0};
sprintf(buffer,"語法分析結果如下:(以四元式形式輸出)\n");
fputs(buffer,fp2);
//printf("語法分析結果如下:(以四元式形式輸出)\n");
scaner();//掃描函數
lrparser();
if(q>19)
printf(" to long sentense!\n");
else
{

for (j=0;j<q;j++)
{
//printf("( %s, %s, %s, %s) \n\n",quad[j].op1,quad[j].ag11,quad[j].ag21,quad[j].result1);
sprintf(buffer,"( %s, %s, %s, %s) \n\n",quad[j].op1,quad[j].ag11,quad[j].ag21,quad[j].result1);
fputs(buffer,fp2);
}
}
printf("已把相應的詞法和語法的結果保存到相應的文件中,請查閱!\n");
fclose(fp1);
fclose(fp2);
}
int lrparser()
{
int schain=0;
kk=0;
if (syn==1) //得到begin
{
scaner();//掃描下個字元
schain=yucu();
if(syn==6)//得到end
{
scaner();//掃描下個字元
if((syn==0)&&(kk==0)) //得到#
printf("Success!\n");
}
else
{
if(kk!=1)
printf("short of 'end' !\n");
kk=1;
getch();
exit(0);
}
}
else
{
printf("short of 'begin' !\n");
kk=1;
getch();
exit(0);
}
return (schain);
}
int yucu()
{
int schain=0;
schain=statement();
while(syn==26)
{
scaner();
schain=statement();
}
return (schain);
}
int statement()
{
char tt[8],eplace[8];
int schain=0;
if (syn==10)
{
strcpy(tt,token); //tt中保存的是第一個字元
scaner();
if(syn==18) //檢測到=號
{
scaner();
strcpy(eplace,expression());
emit(tt,eplace,"=","__");
schain=0;
}
else
{
printf("short of sign ':=' !\n");
kk=1;
getch();
exit(0);
}
return (schain);
}
}
char *expression()
{
char *tp,*ep2,*eplace,*tt;
tp=(char *)malloc(12);
ep2=(char *)malloc(12);
eplace=(char *)malloc(12);
tt=(char *)malloc(12);

strcpy(eplace,term());

while((syn==13)||(syn==14))
{
if (syn==13)
strcpy(tt,"+");
else
strcpy(tt,"-");

scaner();
strcpy(ep2,term());
strcpy(tp,newtemp());
emit(tp,eplace,tt,ep2);
strcpy(eplace,tp);
}
return (eplace);
}
char *term()
{
char *tp,*ep2,*eplace,*tt;
tp=(char *)malloc(12);
ep2=(char *)malloc(12);
eplace=(char *)malloc(12);
tt=(char *)malloc(12);

strcpy(eplace,factor());

while((syn==15)||(syn==16))
{
if (syn==15)
strcpy(tt,"*");
else
strcpy(tt,"/");
scaner();
strcpy(ep2,factor());
strcpy(tp,newtemp());
emit(tp,eplace,tt,ep2);
strcpy(eplace,tp);
}
return (eplace);
}
char *factor()
{
char *fplace;
fplace=(char *)malloc(12);
strcpy(fplace,"");

if(syn==10) //得到字元
{
strcpy(fplace,token);
scaner();
}
else if(syn==11) //得到數字
{
itoa(sum,fplace,10);
scaner();
}
else if(syn==27) //得到)
{
scaner();
fplace=expression();
if(syn==28) //得到(
scaner();
else
{
printf("error on ')' !\n");
kk=1;
getch();
exit(0);
}
}
else
{
printf("error on '(' !\n");
kk=1;
getch();
exit(0);
}
return (fplace);
}
//該函數回送一個新的臨時變數名,臨時變數名產生的順序為T1,T2...
char *newtemp()
{
char *p;
char m[8];
p=(char *)malloc(8);

kk++;
itoa(kk,m,10);
strcpy(p+1,m);
p[0]='t';
return(p); //設置中間變數名放在一個字元數組中,字元數組的第一個字元為t第二個字元為m表示的數值
}
void scaner()
{
sum=0;
///for(m=0;m<8;m++)
//token[m++]=NULL;
memset(token,0,8);
m=0;
ch=prog[p++];
while(ch==' ')
ch=prog[p++];
if(((ch<='z')&&(ch>='a'))||((ch<='Z')&&(ch>='A')))
{
while(((ch<='z')&&(ch>='a'))||((ch<='Z')&&(ch>='A'))||((ch>='0')&&(ch<='9')))
{
token[m++]=ch;
ch=prog[p++];
}
p--;
syn=10;
token[m++]='\0';
for(n=0;n<6;n++)
if(strcmp(token,rwtab[n])==0)
{
syn=n+1;
break;
}
}
else if((ch>='0')&&(ch<='9'))
{
while((ch>='0')&&(ch<='9'))
{
sum=sum*10+ch-'0';
ch=prog[p++];
}
p--;
syn=11;
}
else switch(ch)
{
case '<':m=0;
ch=prog[p++];
if(ch=='>')
{
syn=21;
}
else if(ch=='=')
{
syn=22;
}
else
{
syn=20;
p--;
}
break;
case '>':m=0;
ch=prog[p++];
if(ch=='=')
{
syn=24;
}
else
{
syn=23;
p--;
}
break;
case ':':m=0;
token[m++] = ch;
ch=prog[p++];
if(ch=='=')
{
syn=18;
token[m++] = ch;
}
else
{
syn=17;
p--;
}
break;

case '+': syn=13;token[0] = ch; break;
case '-': syn=14;token[0] = ch; break;
case '*': syn=15;token[0] = ch;break;
case '/': syn=16;token[0] = ch;break;
case '(': syn=27;token[0] = ch;break;
case ')': syn=28;token[0] = ch;break;
case '=': syn=25;token[0] = ch;break;
case ';': syn=26;token[0] = ch;break;
case '#': syn=0;token[0] = ch;break;
default: syn=-1;break;
}
}
//該函數是生成一個三地址語句送到四元式表中
void emit(char *result,char *ag1,char *op,char *ag2)
{
strcpy(quad[q].result1,result);
strcpy(quad[q].ag11,ag1);
strcpy(quad[q].op1,op);
strcpy(quad[q].ag21,ag2);
q++; //統計有多少個四元式
}

熱點內容
浙江伺服器dns地址雲空間 發布:2024-10-27 03:31:19 瀏覽:676
編譯器的讀音 發布:2024-10-27 03:31:11 瀏覽:473
逆水寒和魔獸哪個配置高 發布:2024-10-27 03:30:35 瀏覽:907
java可變長度數組 發布:2024-10-27 03:30:35 瀏覽:400
linux查詢命令的版本 發布:2024-10-27 03:24:38 瀏覽:976
編程3次方 發布:2024-10-27 03:19:48 瀏覽:19
如何提取手機緩存視頻 發布:2024-10-27 02:55:26 瀏覽:370
php二維數組求和 發布:2024-10-27 02:53:56 瀏覽:734
c語言如何被編譯器編成可執行 發布:2024-10-27 02:33:27 瀏覽:555
解壓蜂巢 發布:2024-10-27 02:32:45 瀏覽:184