前言&思路
? ? ? ? 詞法分析器不用多說,一開始我還不知道是什么樣的,看了下別人的博客,再看看書,原來是輸出二元組,這不就是字符串操作嘛。然后細(xì)看幾篇博客,發(fā)現(xiàn)大都是用暴力判斷來寫的。我對代碼重復(fù)性比較高的方法不太感冒,不是說我編程有多好,就是單純的不喜歡。
????????于是我就想到了用偏移量的方法,先弄好單詞分類器,再用幾個數(shù)組將關(guān)鍵字、運(yùn)算符、界符的所有定義的字符串保存為全局變量,順序要按單詞分類器的順序,然后根據(jù)這三種字符串在表中的位置,定一個偏移量。當(dāng)輸入的時候,將字符串進(jìn)行切割提取,然后放到全局?jǐn)?shù)組里面判斷,返回下標(biāo)加上偏移量,這樣就可以得到這一個字符串在單詞表中的位置(種別碼)了,最后根據(jù)種別碼的范圍可以知道數(shù)據(jù)類型。
? ? ? ? 上面的思路說起來比較復(fù)雜,主要是我表達(dá)能力不太好,其實(shí)一看代碼就明白了。
????????哦對了,補(bǔ)充一句,我要分析的語言是C語言,然后也是用C語言來寫。但是因?yàn)橛惺褂貌紶栴愋?,所以需要C++的運(yùn)行環(huán)境。另外,我的代碼沒有代碼檢錯。(老師沒有要求)
NFA和DFA圖
? ? ? ? 這兩個圖不是我畫的,看看就好。????????
? ? ? ? 我要寫的詞法分析器有六個類型:關(guān)鍵字、界符、運(yùn)算符、標(biāo)識符、整型和浮點(diǎn)型。
? ? ? ?
?單詞分類表
? ? ? ? 關(guān)鍵字在網(wǎng)上找了32個,然后自己加了幾個,運(yùn)算符和界符都是百度找的,其中,運(yùn)算符有>>=這種三個字符的,因?yàn)椴皇潜匾奈揖蛣h去了,這樣方便寫代碼
單詞符號 |
種類 |
種別碼 |
auto |
關(guān)鍵字 |
1 |
bool |
關(guān)鍵字 |
2 |
break |
關(guān)鍵字 |
3 |
case |
關(guān)鍵字 |
4 |
char |
關(guān)鍵字 |
5 |
const |
關(guān)鍵字 |
6 |
continue |
關(guān)鍵字 |
7 |
default |
關(guān)鍵字 |
8 |
do |
關(guān)鍵字 |
9 |
double |
關(guān)鍵字 |
10 |
else |
關(guān)鍵字 |
11 |
enum |
關(guān)鍵字 |
12 |
extern |
關(guān)鍵字 |
13 |
float |
關(guān)鍵字 |
14 |
for |
關(guān)鍵字 |
15 |
goto |
關(guān)鍵字 |
16 |
if |
關(guān)鍵字 |
17 |
int |
關(guān)鍵字 |
18 |
long |
關(guān)鍵字 |
19 |
main |
關(guān)鍵字 |
20 |
register |
關(guān)鍵字 |
21 |
return |
關(guān)鍵字 |
22 |
short |
關(guān)鍵字 |
23 |
signed |
關(guān)鍵字 |
24 |
sizeof |
關(guān)鍵字 |
25 |
static |
關(guān)鍵字 |
26 |
struct |
關(guān)鍵字 |
27 |
switch |
關(guān)鍵字 |
28 |
typedef |
關(guān)鍵字 |
29 |
union |
關(guān)鍵字 |
30 |
unsigned |
關(guān)鍵字 |
31 |
void |
關(guān)鍵字 |
32 |
volatile |
關(guān)鍵字 |
33 |
while |
關(guān)鍵字 |
34 |
scanf |
關(guān)鍵字 |
35 |
printf |
關(guān)鍵字 |
36 |
include |
關(guān)鍵字 |
37 |
define |
關(guān)鍵字 |
38 |
+ |
運(yùn)算符 |
39 |
- |
運(yùn)算符 |
40 |
* |
運(yùn)算符 |
41 |
/ |
運(yùn)算符 |
42 |
% |
運(yùn)算符 |
43 |
++ |
運(yùn)算符 |
44 |
-- |
運(yùn)算符 |
45 |
== |
運(yùn)算符 |
46 |
!= |
運(yùn)算符 |
47 |
> |
運(yùn)算符 |
48 |
< |
運(yùn)算符 |
49 |
>= |
運(yùn)算符 |
50 |
<= |
運(yùn)算符 |
51 |
&& |
運(yùn)算符 |
52 |
|| |
運(yùn)算符 |
53 |
! |
運(yùn)算符 |
54 |
= |
運(yùn)算符 |
55 |
+= |
運(yùn)算符 |
56 |
-= |
運(yùn)算符 |
57 |
*= |
運(yùn)算符 |
58 |
/= |
運(yùn)算符 |
59 |
%= |
運(yùn)算符 |
60 |
&= |
運(yùn)算符 |
61 |
^= |
運(yùn)算符 |
62 |
|= |
運(yùn)算符 |
63 |
& |
運(yùn)算符 |
64 |
| |
運(yùn)算符 |
65 |
^ |
運(yùn)算符 |
66 |
~ |
運(yùn)算符 |
67 |
<< |
運(yùn)算符 |
68 |
>> |
運(yùn)算符 |
69 |
( |
界符 |
70 |
) |
界符 |
71 |
[ |
界符 |
72 |
] |
界符 |
73 |
{ |
界符 |
74 |
} |
界符 |
75 |
. |
界符 |
76 |
, |
界符 |
77 |
; |
界符 |
78 |
’ |
界符 |
79 |
# |
界符 |
80 |
? |
界符 |
81 |
’’ |
界符 |
82 |
整型 |
整型 |
83 |
浮點(diǎn)型 |
浮點(diǎn)型 |
84 |
標(biāo)識符 |
標(biāo)識符 |
85 |
?具體代碼解釋
? ? ? ? 下面是定義的全局變量,因?yàn)?-*/這些符號比較特殊,在VS里面運(yùn)行會報錯,所以我使用[]將它括起來,主要看報不報錯吧,不報錯可以直接寫。
? ? ? ? 我這樣用全局變量寫的好處就是,當(dāng)我需要添加關(guān)鍵字或者其他什么的時候,只需要修改下面這一部分的代碼就好。
? ? ? ? 然后下面是函數(shù)的聲明,具體就放在后面的源代碼中了,看注釋也可以很清楚的知道是做什么的。
? ? ? ? ?其他都沒什么好說的,還有一點(diǎn)就是下面,因?yàn)橐祷匾粋€字符串?dāng)?shù)組,所以我用靜態(tài)局部變量來返回。
2023.3.26日補(bǔ)充:
? ? ? ? 有一個很關(guān)鍵的點(diǎn)忘記說了,這個思路還是挺重要的,就是如何不用暴力判斷的方法提取出運(yùn)算符。運(yùn)算符的提取是在這個位置:
? ? ? ?看一下代碼,是不是特別簡單?原本30個運(yùn)算符只需要這十來行代碼就可以從字符串中提取出來。這個思路我是覺得很好的,下面來講一下我的思路。
????????因?yàn)檫\(yùn)算符只有兩種長度,1或者2(之前是有長度為3的,因?yàn)闆]必要我刪掉了)。所以一開始我也是想直接暴力判斷的,就是看第一位是什么,再看下一位是什么,能不能湊出和運(yùn)算符里面一樣的,然后差不多寫完的時候發(fā)現(xiàn),代碼太多了,而且重復(fù)量很大,所以我就想能不能簡單一點(diǎn)呢?然后我就簡單列了個表。
+? ? 后綴:+,=
-? ? ?后綴:-,=
*? ? ?后綴:=
/? ? ?后綴:=
%? ?后綴:=
=? ? 后綴:=
!? ? ?后綴:=
>? ? 后綴:>,=
<? ? 后綴:<,=
&? ? 后綴:&,=
|? ? ?后綴:|,=
^? ? 后綴:=
~? ? 后綴:無
? ? ? ? ?列完了我就發(fā)現(xiàn),除了一個單獨(dú)會出現(xiàn)的~運(yùn)算符外,其他運(yùn)算符都是由單個運(yùn)算符組合起來的,而且組合的話,要么第一個字符和第二個字符相等,要么就是第二個字符就是=(等號),按照這個思路,寫出來的代碼不就簡單多了?解釋完畢,應(yīng)該很好理解哈哈。
????????更多的不進(jìn)行分析,自行看代碼注釋。
源代碼
? ? ? ? 用dev-c++或者vs、vsc都可以運(yùn)行。(要是報錯自己解決不了的可以找我)代碼大概是160行,比其他博客少挺多的了,當(dāng)然,我們老師沒有要求我們進(jìn)行代碼的檢錯,所以我這就是很簡單的字符串操作。(注意:使用VS運(yùn)行代碼的話需要創(chuàng)建項目,然后關(guān)閉SDL檢查,很多人不常用VS不知道,關(guān)閉步驟:打開項目后,點(diǎn)擊項目->屬性->->C/C++->常規(guī)->SDL檢查->將是改為否->點(diǎn)擊應(yīng)用后確定,不關(guān)掉SDL檢查的話很多常用的函數(shù)都會報錯說不安全)
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define Maxline 1024
//-----全局變量------
char keyword[][10] = { "auto","bool","break","case","char","const","continue","default","do"
,"double","else","enum","extern","float","for","goto","if","int","long","main","register","return"
,"short","signed","sizeof","static","struct","switch","typedef","union","unsigned","void","volatile"
,"while","scanf","printf","include","define" }; //38個保留字(關(guān)鍵字)
char delimiter[][10] = { "(",")","[","]","{","}",".",",",";","'","#","?",'"', };//13個界符
char calculation[][10] = { "[+]","[-]","[*]","[/]","[%]","[++]","[--]","[==]","[!=]","[>]","[<]","[>=]"
,"[<=]","[&&]","[||]","[!]","[=]","[+=]","[-=]","[*=]","[/=]","[%=]","[&=]","[^=]","[|=]","[&]","[|]"
,"[^]","[~]","[<<]","[>>]"};//31個運(yùn)算符
int nk = 38, nd = 13, nc = 31;//分別表示關(guān)鍵字、界符、運(yùn)算符的個數(shù)
//下面分別是關(guān)鍵字、界符、運(yùn)算符、整型、浮點(diǎn)數(shù)和標(biāo)識符在單詞表中的偏移量
int ck = 1, cd = 70, cc = 39, ci = 83, cf = 84, cn = 85;
//------函數(shù)聲明------
bool pd_integer(char ch); //判斷是否是整數(shù)
bool pd_character(char ch); //判斷是否是字母
int pd_keyword(char s[]); //判斷字符串是否是保留字(關(guān)鍵字)是則返回下標(biāo),否則返回-1
int pd_delimiter(char ch); //判斷字符是否是界符,是返回下標(biāo),否則返回-1
int pd_calculation(char s[]); //判斷字符串是否是運(yùn)算符,是返回下標(biāo),否則返回-1
char* pd_spcode(int n); //根據(jù)種別碼的范圍判斷類別
int search_spcode(char s[]); //查詢字符串s的種別碼
//------主函數(shù)------
int main() {
char test[1030] = { 0 };
FILE* fp = fopen("test.txt", "r");
if (fp == NULL) {
printf("打開文件失敗!\n");
return 0;
}
while (fgets(test, Maxline, fp) != NULL) {
int i = 0, j = 0;
while (i < strlen(test)) {
if (test[i] == ' ' || test[i] == '\n' || test[i] == '\t') {
i++;
continue;//遇到空格或換行符,跳過
}
bool flag = true;
char str[100] = { 0 };
j = 0;
if (pd_integer(test[i])) {
//如果是數(shù)字,循環(huán)讀取,包括小數(shù)點(diǎn),因?yàn)榭赡苁歉↑c(diǎn)數(shù)
while (pd_integer(test[i]) || (test[i] == '.' && flag)) {
if (test[i] == '.')flag = false;//浮點(diǎn)型只允許出現(xiàn)一個小數(shù)點(diǎn)
str[j++] = test[i++];
}
i--;
}
else if (pd_character(test[i]) || test[i] == '_') {
//如果是字母或下劃線或數(shù)字(標(biāo)識符可以有數(shù)字)
while (pd_character(test[i]) || test[i] == '_' || pd_integer(test[i]))str[j++] = test[i++];
i--;
}
else if (test[i] == '+' || test[i] == '-' || test[i] == '*' || test[i] == '/'
|| test[i] == '%' || test[i] == '=' || test[i] == '!' || test[i] == '>'
|| test[i] == '<' || test[i] == '&' || test[i] == '|' || test[i] == '^') {
if (test[i + 1] == '=' || test[i] == test[i + 1]) {
str[0] = test[i], str[1] = test[i + 1];
i++;
}
else {
str[0] = test[i];
}
}
else {
str[0] = test[i];
}
i++;
printf("( %s , %d )------%s\n", str, search_spcode(str), pd_spcode(search_spcode(str)));
}
}
return 0;
}
//------函數(shù)實(shí)現(xiàn)------
bool pd_integer(char ch) {//判斷是否是整數(shù)
if (ch >= '0' && ch <= '9')return true;
return false;
}
bool pd_character(char ch) {//判斷是否是字母
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))return true;
return false;
}
int pd_keyword(char s[]) {//判斷字符串是否是保留字(關(guān)鍵字)是則返回下標(biāo),否則返回-1
for (int i = 0; i < nk; i++)
if (strcmp(s, keyword[i]) == 0)return i;
return -1;
}
int pd_delimiter(char ch) {//判斷字符是否是界符,是返回下標(biāo),否則返回-1
for (int i = 0; i < nd; i++)
if (ch == delimiter[i][0])return i;
return -1;
}
int pd_calculation(char s[]) {//判斷字符是否是運(yùn)算符,是返回下標(biāo),否則返回-1
for (int i = 0; i < nc; i++) {
if (strlen(calculation[i]) - 2 == strlen(s)) {//如果長度相等
bool flag = true;
for (int j = 1, k = 0; j < strlen(calculation[i]) - 1 && k < strlen(s); j++, k++)
if (calculation[i][j] != s[k]) {
flag = false;//有一個元素不等,標(biāo)記后直接退出
break;
}
if (flag)return i;//返回下標(biāo)
}
}
return -1;
}
char* pd_spcode(int n) { //根據(jù)種別碼的范圍判斷類別
static char name[20];//靜態(tài)局部變量
if (n >= 1 && n <= nk) strcpy(name, "關(guān)鍵字");
else if (n >= cc && n < cd) strcpy(name, "運(yùn)算符");
else if (n >= cd && n < ci) strcpy(name, "界符");
else if (n == ci) strcpy(name, "整型");
else if (n == cf)strcpy(name, "浮點(diǎn)型");
else if (n == cn) strcpy(name, "標(biāo)識符");
else strcpy(name, "未識別");
return name;
}
int search_spcode(char s[]) { //查詢字符串s的種別碼
if (pd_character(s[0])) { //如果第一個字符是字母,說明是關(guān)鍵字或標(biāo)識符
if (pd_keyword(s) != -1) //說明是關(guān)鍵字
return pd_keyword(s) + ck;//下標(biāo)從0開始,要+1
else return cn; //否則就是標(biāo)識符(標(biāo)識符可以有下劃線)
}
if (s[0] == '_')return cn;//下劃線開頭一定是標(biāo)識符
if (pd_integer(s[0])) {//開頭是數(shù)字說明是整數(shù)或者浮點(diǎn)數(shù)
if (strstr(s, ".") != NULL)return cf;//如果有小數(shù)點(diǎn)說明是浮點(diǎn)型
return ci;//否則就是整型
}
if (strlen(s) == 1) { //長度為1,說明是界符或者運(yùn)算符
char ch = s[0];
if (pd_delimiter(ch) != -1)//判斷是否是界符
return pd_delimiter(ch) + cd;
}
if (pd_calculation(s) != -1)//如果是運(yùn)算符
return pd_calculation(s) + cc;
return -1;//否則就是未標(biāo)識符
}
運(yùn)行結(jié)果展示
????????運(yùn)行后代碼是使用文件進(jìn)行輸入的。
????????代碼中最好不要有中文哈,因?yàn)闆]有處理中文,會有一堆未識別出來的(一個中文站字符串?dāng)?shù)組的兩個位置)然后頭文件里面,stdio.h里面的點(diǎn)可以不用理會,拆開就拆開了,老師不會考察這個的。
#include<stdio.h>
#include<string.h>
int main()
{
int n,_m,k=666;
scanf("%d %d",&n,&_m);
printf("%d+%d=%d",n,_m,n+_m);
return 0;
}
運(yùn)行結(jié)果:
?實(shí)驗(yàn)驗(yàn)收的時候不會有帶有頭文件和函數(shù)的,就算有也是加幾個關(guān)鍵字就行。
詞法分析器驗(yàn)收
? ? ? ? 我們老師的驗(yàn)收方式是當(dāng)場給出題目,然后現(xiàn)場改代碼,上完課改不完的估計是會扣分了。然后不需要寫實(shí)驗(yàn)報告啥的,有好有壞吧。
? ? ? ? 如果你的老師也是說要出題目現(xiàn)場改代碼的話,可以借鑒一下這個。
? ? ? ? 我也是今天驗(yàn)收了之后才來寫這篇博客的。
題目:在源語言中,增加新的數(shù)據(jù)類型int36,請實(shí)現(xiàn)對下面代碼段的詞法分析
int36 a=A7,b=5TU,z=A+B;
int c=10;
for(int i=0;i<10;i++){
? ? ? ? if(c%2==0)z=z+10;
? ? ? ? else z=z+1Z;
? ? ? ? c=c+1;
}
說明:
1、int36類型的常量都是大寫,可以是字母開頭,也可以是數(shù)字開頭;可以全是數(shù)字,也可以全是大寫字母。
2、除了int36的常量會大寫外,凡是帶有字母的都是小寫字母。
3、int36當(dāng)作關(guān)鍵字輸出
例如:上面的代碼中:z=z+10;中10是int36類型的,同類型才能相加,需要進(jìn)行輸出說明。
? ? ? ? ?題目分析:
? ? ? ? 1、問題主要是如何標(biāo)記int36類型的常量和區(qū)分代碼中的3個10。
? ? ? ? 2、如果提取的字符串中帶有大寫字母,那么一定是int36類型的常量
? ? ? ? 3、直接分析10這種整型是int36還是int不現(xiàn)實(shí),所以需要將int36類型的變量名稱進(jìn)行標(biāo)記,可以將變量復(fù)制語句里面的等號前面的字母/數(shù)字/下劃線提取出來,放到一個數(shù)組里面,當(dāng)遇到完全是數(shù)字的時候進(jìn)行判斷,看等號前面的變量名在不在數(shù)組里面,如果在的話,說明這個整型其實(shí)是int36類型的常量,進(jìn)行標(biāo)記方便后面進(jìn)行輸出(后面輸出的時候要取消標(biāo)記)(目前我只想到這個方法,有其他想法的可以評論交流)
? ? ? ? 簡單的幾句分析,代碼還是要進(jìn)行挺大的改動的(只是在原有功能上面添加,原本的功能不變)。源代碼就不進(jìn)行解釋了,相信大部分同學(xué)都是不用進(jìn)行驗(yàn)收的。
驗(yàn)收后的源代碼
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define Maxline 1024
//-----全局變量------
char keyword[][10] = { "auto","bool","break","case","char","const","continue","default","do"
,"double","else","enum","extern","float","for","goto","if","int","long","main","register","return"
,"short","signed","sizeof","static","struct","switch","typedef","union","unsigned","void","volatile"
,"while","scanf","printf","include","define","int36"}; //39個保留字(關(guān)鍵字)
char delimiter[][10] = {"(",")","[","]","{","}",".",",",";","'","#","?",'"',};//13個界符
char calculation[][10] = {"[+]","[-]","[*]","[/]","[%]","[++]","[--]","[==]","[!=]","[>]","[<]","[>=]"
,"[<=]","[&&]","[||]","[!]","[=]","[+=]","[-=]","[*=]","[/=]","[%=]","[&=]","[^=]","[|=]","[&]","[|]"
,"[^]","[~]","[<<]","[>>]"};//31個運(yùn)算符
int nk = 39, nd = 13, nc = 31;//分別表示關(guān)鍵字、界符、運(yùn)算符的個數(shù)
//下面分別是關(guān)鍵字、界符、運(yùn)算符、整型、浮點(diǎn)數(shù)和標(biāo)識符在單詞表中的偏移量,int36類型為87;
int ck = 1, cd = 71, cc = 40, ci = 84, cf = 85, cn = 86, cint36 = 87;
char int36[100][100]; //int36類型的變量標(biāo)識符
int p = 0;
bool flag3 = false, flag4 = false;//用于標(biāo)記
//------函數(shù)聲明------
bool pd_integer(char ch); //判斷是否是數(shù)字
bool pd_character(char ch); //判斷是否是小寫字母
bool pd_character2(char ch); //判斷是否是大寫字母
int pd_keyword(char s[]); //判斷字符串是否是保留字(關(guān)鍵字)是則返回下標(biāo),否則返回-1
int pd_delimiter(char ch); //判斷字符是否是界符,是返回下標(biāo),否則返回-1
int pd_calculation(char s[]); //判斷字符串是否是運(yùn)算符,是返回下標(biāo),否則返回-1
char* pd_spcode(int n); //根據(jù)種別碼的范圍判斷類別
bool pd_int36(char s[]); //判斷是否是int36類型的變量
int search_spcode(char s[]); //查詢字符串s的種別碼
//------主函數(shù)------
int main(int argc,void *argv) {
char test[1030] = { 0 };
FILE* fp = fopen("test.txt", "r");
if (fp == NULL) {
printf("打開文件失敗!\n");
return 0;
}
while (fgets(test, Maxline, fp) != NULL) {
int i = 0, j = 0;
while (i < strlen(test)) {
if (test[i] == ' ' || test[i] == '\n' || test[i] == '\t') {
i++;
continue;//遇到空格或換行符,跳過
}
bool flag = true;
char str[100] = { 0 };
j = 0;
if (test[i] == 'i' && test[i + 1] == 'n' && test[i + 2] == 't' && test[i + 3] == '3' && test[i + 4] == '6') {
str[j++] = test[i];
str[j++] = test[i + 1];
str[j++] = test[i + 2];
str[j++] = test[i + 3];
str[j] = test[i + 4];
i += 4;
//printf("%s\n", str);
}
else if (pd_character2(test[i])) {
while (pd_character2(test[i]) || pd_integer(test[i]))str[j++] = test[i++];
i--;
}
else if (pd_integer(test[i])) {
//如果是數(shù)字,循環(huán)讀取,包括小數(shù)點(diǎn),因?yàn)榭赡苁歉↑c(diǎn)數(shù)
int k = i;
bool flag2 = false;
while (pd_integer(test[k]) || pd_character2(test[k])) {
if (pd_character2(test[k]))flag2 = true;
k++;
}
if (flag2) {
while (pd_integer(test[i]) || pd_character2(test[i])) {
if (test[i] == '.')flag = false;//浮點(diǎn)型只允許出現(xiàn)一個小數(shù)點(diǎn)
str[j++] = test[i++];
}
}
else {
while (pd_integer(test[i]) || (test[i] == '.' && flag)) {
if (test[i] == '.')flag = false;//浮點(diǎn)型只允許出現(xiàn)一個小數(shù)點(diǎn)
str[j++] = test[i++];
}
}
i--;
if (search_spcode(str) == ci) {//如果是整型,判斷是否是int36類型
k = i;
int l = 0;
char str1[20] = { 0 }, str2[20] = { 0 };//用一個新建的變量來保存變量
while (test[k] != '=' || test[k] == ' ')k--;//test[k]=='=';
k--;
while (pd_integer(test[k]) || pd_character(test[k]) || test[k] == '_') {
str1[l++] = test[k];
k--;
}
//將變量名倒過來
int jj = 0;
for (int ii = 0; ii < l; ii++) {
str2[jj++] = str1[l - ii - 1];
}
if (pd_int36(str2)) flag4 = true;
}
}
else if (pd_character(test[i]) || test[i] == '_') {
//如果是字母或下劃線或數(shù)字(標(biāo)識符可以有數(shù)字)
while (pd_character(test[i]) || test[i] == '_'||pd_integer(test[i]))str[j++] = test[i++];
i--;
}
else if (test[i] == '+' || test[i] == '-' || test[i] == '*' || test[i] == '/'
|| test[i] == '%' || test[i] == '=' || test[i] == '!' || test[i] == '>'
|| test[i] == '<' || test[i] == '&' || test[i] == '|' || test[i] == '^') {
if (test[i + 1] == '=' || test[i] == test[i + 1]) {
str[0] = test[i], str[1] = test[i+1];
i++;
}
else {
str[0] = test[i];
}
}
else {
str[0] = test[i];
}
i++;
if (strcmp(str, "int36") == 0 && !flag3)flag3 = true;
else if (str[0] == ';' && flag3)flag3 = false;
if (flag3 && (str[0] == '_' || pd_character(str[0]))) {
if (!pd_int36(str))strcpy(int36[p++], str);//如果變量不在數(shù)組里面,就放進(jìn)去
}
int a = search_spcode(str);//用一個變量臨時保存種別碼
printf("( %s , %d )------%s\n", str, a, pd_spcode(a));
}
}
//for (int i = 0; i < p; i++)printf("%s\n", int36[i]);//測試用
return 0;
}
//------函數(shù)實(shí)現(xiàn)------
bool pd_integer(char ch) {//判斷是否是整數(shù)
if (ch >= '0' && ch <= '9')return true;
return false;
}
bool pd_character(char ch) {//判斷是否是小寫字母
if ((ch >= 'a' && ch <= 'z'))return true;
return false;
}
bool pd_character2(char ch) {//判斷是否是大寫字母
if ((ch >= 'A' && ch <= 'Z'))return true;
return false;
}
int pd_keyword(char s[]) {//判斷字符串是否是保留字(關(guān)鍵字)是則返回下標(biāo),否則返回-1
for (int i = 0; i < nk; i++)
if (strcmp(s, keyword[i]) == 0)return i;
return -1;
}
int pd_delimiter(char ch) {//判斷字符是否是界符,是返回下標(biāo),否則返回-1
for (int i = 0; i < nd; i++)
if (ch == delimiter[i][0])return i;
return -1;
}
int pd_calculation(char s[]) {//判斷字符是否是運(yùn)算符,是返回下標(biāo),否則返回-1
for (int i = 0; i < nc; i++) {
if (strlen(calculation[i]) - 2 == strlen(s)) {//如果長度相等
bool flag = true;
for(int j=1,k=0;j<strlen(calculation[i])-1&&k<strlen(s);j++,k++)
if (calculation[i][j] != s[k]) {
flag = false;//有一個元素不等,標(biāo)記后直接退出
break;
}
if (flag)return i;//返回下標(biāo)
}
}
return -1;
}
bool pd_int36(char s[]) {//判斷是否是int36類型的變量
for (int i = 1; i < p; i++)
if (strcmp(int36[i], s) == 0) return true;
return false;
}
char* pd_spcode(int n) { //根據(jù)種別碼的范圍判斷類別
static char name[20];//靜態(tài)局部變量
if (n >= 1 && n <= nk) strcpy(name, "關(guān)鍵字");
else if (n >= cc && n < cd) strcpy(name, "運(yùn)算符");
else if (n >= cd && n < ci) strcpy(name, "界符");
else if (n == ci) strcpy(name, "整型");
else if (n == cf)strcpy(name, "浮點(diǎn)型");
else if (n == cn) strcpy(name, "標(biāo)識符");
else if (n == cint36) {
strcpy(name, "int36");
flag4 = false;//取消標(biāo)記
}
else strcpy(name,"未識別");
return name;
}
int search_spcode(char s[]) { //查詢字符串s的種別碼
if (pd_character2(s[0]))return cint36;
for (int i = 0; i < strlen(s); i++)
if (pd_character2(s[i])) return cint36;
if (pd_character(s[0])) { //如果第一個字符是小寫字母,說明是關(guān)鍵字或標(biāo)識符
if (pd_keyword(s) != -1) //說明是關(guān)鍵字
return pd_keyword(s) + ck;//下標(biāo)從0開始,要+1
else return cn; //否則就是標(biāo)識符(標(biāo)識符可以有下劃線)
}
if (s[0] == '_')return cn;//下劃線開頭一定是標(biāo)識符
if (pd_integer(s[0])) {//開頭是數(shù)字說明是整數(shù)或者浮點(diǎn)數(shù)
if (flag4) return cint36;//如果被做了標(biāo)記,說明是int36類型
if (strstr(s, ".") != NULL)return cf;//如果有小數(shù)點(diǎn)說明是浮點(diǎn)型
return ci;//否則就是整型
}
if (strlen(s) == 1) { //長度為1,說明是界符或者運(yùn)算符
char ch = s[0];
if (pd_delimiter(ch) != -1)//判斷是否是界符
return pd_delimiter(ch) + cd;
}
if (pd_calculation(s) != -1)//如果是運(yùn)算符
return pd_calculation(s) + cc;
return -1;//否則就是未標(biāo)識符
}
?驗(yàn)收后代碼運(yùn)行結(jié)果
? ? ? ? 我加多了幾條語句,測試結(jié)果都是正確的,應(yīng)該沒有什么問題了。
? ? ? ? 有問題可以評論提問或者私信我。(擔(dān)心代碼有bug)文章來源:http://www.zghlxwxcb.cn/news/detail-459671.html
驗(yàn)收感想
? ? ? ? 平時寫代碼還是太順了,驗(yàn)收的時候竟然卡在了將局部變量當(dāng)成全局變量來用,一直得不到想要的結(jié)果。果然一緊張就會犯錯,還是需要進(jìn)行更多的練習(xí)。要提高自身的抗壓能力。文章來源地址http://www.zghlxwxcb.cn/news/detail-459671.html
到了這里,關(guān)于編譯原理詞法分析器(C/C++)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!