wordpress微信分享縮微圖seo搜索引擎招聘
- 源代碼及附件:編譯原理實驗一源程序及附件資源-CSDN文庫
- 實驗題目
- 實驗要求
- 實驗設計
前兩部分指出了實驗的宏觀把控,為了具體實施實驗,我們需要預先為實驗做出如下設計:
本次實驗我選取了C語言的一個子集進行設計詞法分析器,其中單詞種類如下:(也可參考附件中的單詞種類表)
根據(jù)題目要求,我們用正則文法來定義我們的子集:
- S→關(guān)鍵字|運算符|分界符|整型常量|浮點型常量|標識符
- 關(guān)鍵字
→ void|main|int|float|for|while|switch|case|if|else|return|break
(3)運算符 → +|-|*|/|=|==|<|<=|>|>=
(4)分界符 → (|)|[|]|{|}|,|;|:
(5)整型常量 → digit(digit)*
(6)浮點型常量 → digit(digit).digit(digit)
(7)digit→ 0|1|2|3|4|5|6|7|8|9
(8)letter→ a|b|…|z|A|B|…|Z
(9)標識符 → letter(letter|digit)*
- 實驗分析
基本算法思想是:讀取文件,逐個字符分析,若是空白符則跳過,為字母時將連續(xù)的字母使用超前搜索組合成為變量或關(guān)鍵字;若是數(shù)字,則要判斷是否為浮點數(shù),即利用超前搜索,判斷掃描到的字符是否為小數(shù)點;若是分隔符或者操作符,利用switch語句判斷并輸出,若是其他字符,輸出為未定義的字符。
相關(guān)代碼段:
void lexicalAnalysis(FILE* fp)
{
??? char ch;????????
??? while ((ch = fgetc(fp)) != EOF)??? /
??? {
??????? token = ch;???????????????????????????????????
??????? if (ch == ' ' || ch == '\t' || ch == '\n')? //忽略空格、Tab和回車
??????? {
???????????? if (ch == '\n')??????????????????????????? //遇到換行符,記錄行數(shù)的row加1
???????????????? row++;
???????????? continue;????????????????????????????????
??????? }
??????? else if (isLetter(ch))??????? //以字母開頭,關(guān)鍵字或標識符
??????? {
???????????? token = "";? ???????????????? //token初始化
???????????? while (isLetter(ch) || isDigit(ch)) //非字母或數(shù)字時退出,將單詞存儲在token中
???????????? {
???????????????? token.push_back(ch);? //將讀取的字符ch存入token中
???????????????? ch = fgetc(fp);?????????? //獲取下一個字符
???????????? }
???????????? //文件指針后退一個字節(jié),即重新讀取上述單詞后的第一個字符
???????????? fseek(fp, -1L, SEEK_CUR);
???????????? if (isKey(token)) //關(guān)鍵字
??? ???????????? code = TokenCode(getKeyID(token));
???????????? else //標識符
???????????????? code = TK_IDENT; //單詞為標識符
??????? }
??????? else if (isDigit(ch)) //無符號常數(shù)以數(shù)字開頭
??????? {
???????????? int isdouble = 0; //標記是否為浮點數(shù)
???????????? token = "";????????? //token初始化
???????????? while (isDigit(ch))?? //當前獲取到的字符為數(shù)字
???????????? {
???????????????? token.push_back(ch);????? //讀取數(shù)字,將其存入token中
???????????????? ch = fgetc(fp);?????????????? //從文件中獲取下一個字符
???????????????? //該單詞中第一次出現(xiàn)小數(shù)點
???????????????? if (ch == '.' && isdouble == 0)
???????????????? {
???????????????????? //小數(shù)點下一位是數(shù)字
???????????????????? if (isDigit(fgetc(fp)))
???????????????????? {
???????????????????????? isdouble = 1;??? //標記該常數(shù)中已經(jīng)出現(xiàn)過小數(shù)點
???????????????????????? fseek(fp, -1L, SEEK_CUR);???? //將超前讀取的小數(shù)點后一位重新讀取
???????????????????????? token.push_back(ch);????????? //將小數(shù)點入token中
???????????????????????? ch = fgetc(fp);?????????????? //讀取小數(shù)點后的下一位數(shù)字
???????????????????? }
???????????????? }
???????????? }
???????????? if (isdouble == 1)
???????????????? code = TK_DOUBLE; //單詞為浮點型
???????????? else
???????????????? code = TK_INT;??????????????? //單詞為整型
???????????? //文件指針后退一個字節(jié),即重新讀取常數(shù)后的第一個字符
???????????? fseek(fp, -1L, SEEK_CUR);
??????? }
??????? else switch (ch)
??????? {
???????????? /*運算符*/
??????? case '+': code = TK_PLUS;???? //+加號?????????
???????????? break;
??????? case '-': code = TK_MINUS;??? //-減號
???????????? break;
??????? case '*': code = TK_STAR;???? //*乘號?????
???????????? break;
??????? case '/': code = TK_DIVIDE;??????? //除號
???????????? break;
??????? case '=':
??????? {
???????????? ch = fgetc(fp);?????????????? //超前讀取'='后面的字符
???????????? if (ch == '=')??????????????? //==等于號
???????????? {
???????????????? token.push_back(ch);? //將'='后面的'='存入token中
???????????????? code = TK_EQ;??????? //單詞為"=="
???????????? }
???????????? else {??????????????????????? //=賦值運算符
???????????????? code = TK_ASSIGN;???? //單詞為"="
???????????????? fseek(fp, -1L, SEEK_CUR); //將超前讀取的字符重新讀取
???????????? }
??????? }
??????? break;
??????? case '<':
??????? {
???????????? ch = fgetc(fp);?????????????? //超前讀取'<'后面的字符
???????????? if (ch == '=')??????????????? //<=小于等于號
???????????? {
???????????????? token.push_back(ch);? //將'<'后面的'='存入token中
???????????????? code = TK_LEQ;??????????? //單詞為"<="
???????????? }
???????????? else {??????????????????????? //<小于號
???????????????? code = TK_LT;??????? //單詞為"<"
???????????????? fseek(fp, -1L, SEEK_CUR); //將超前讀取的字符重新讀取
???????????? }
??????? }
??????? break;
??????? case '>':
??????? {
???????????? ch = fgetc(fp);?????????????? //超前讀取'>'后面的字符
???????????? if (ch == '=')??????????????? //>=大于等于號
???????????? {
???????????????? token.push_back(ch);? //將'>'后面的'='存入token中
???????????????? code = TK_GEQ;??????????? //單詞為">="
???????????? }
???????????? else {??????????????????????? //>大于號
???????????????? code = TK_GT;??????? //單詞為">"
???????????????? fseek(fp, -1L, SEEK_CUR); //將超前讀取的字符重新讀取
???????????? }
??????? }
??????? break;
??????? /*分界符*/
??????? case '(': code = TK_OPENPA;??????? //(左圓括號
???????????? break;
??????? case ')': code = TK_CLOSEPA;?? //)右圓括號
???????????? break;
??????? case '[': code = TK_OPENBR;??????? //[左中括號
???????????? break;
??????? case ']': code = TK_CLOSEBR;?? //]右中括號
???????????? break;
??????? case '{': code = TK_BEGIN;??? //{左大括號
???????????? break;
??????? case '}': code = TK_END;????? //}右大括號
???????????? break;
??????? case ',': code = TK_COMMA;??? //,逗號
???????????? break;
??????? case ';': code = TK_SEMOCOLOM; //;分號
???????????? break;
??????? case':':code = TK_MAO;//:冒號
???????????? break;
???????????? //未識別符號
??????? default: code = TK_UNDEF;
??????? }
??????? print(code);????????????? //打印詞法分析結(jié)果
??? }
}
定義了如下數(shù)據(jù)結(jié)構(gòu)作為狀態(tài)終態(tài):
??? /* 關(guān)鍵字 */
??? KW_VOID, //void關(guān)鍵字
??? KW_MAIN, //main關(guān)鍵字
??? KW_INT,????? //int關(guān)鍵字
??? KW_DOUBLE,?? //double關(guān)鍵字
??? KW_FOR,????? //for關(guān)鍵字
??? KW_WHILE,??? //while關(guān)鍵字
??? KW_SWITCH,?? //switch關(guān)鍵字
??? KW_CASE, //case關(guān)鍵字
??? KW_IF,?????? //if關(guān)鍵字
??? KW_ELSE, //else關(guān)鍵字
??? KW_RETURN,?? //return關(guān)鍵字
??? KW_BREAK,//break關(guān)鍵字
??? /* 運算符 */
??? TK_PLUS, //+加號
??? TK_MINUS,??? //-減號
??? TK_STAR, //*乘號
??? TK_DIVIDE,?? ///除號
??? TK_ASSIGN,?? //=賦值運算符
??? TK_EQ,?????? //==等于號
??? TK_LT,?????? //<小于號
??? TK_LEQ,????? //<=小于等于號
??? TK_GT,?????? //>大于號
??? TK_GEQ,????? //>=大于等于號
??? TK_MAO,??? //:冒號
??? /* 分隔符 */
??? TK_OPENPA,?? //(左圓括號
??? TK_CLOSEPA,? //)右圓括號
??? TK_OPENBR,?? //[左中括號
??? TK_CLOSEBR,? //]右中括號
??? TK_BEGIN,??? //{左大括號
??? TK_END,????? //}右大括號
??? TK_COMMA,??? //,逗號
??? TK_SEMOCOLOM, //;分號
??? /* 常量 */
??? TK_INT,????? //整型常量
??? TK_DOUBLE,?? //浮點型常量
??? /* 標識符 */
TK_IDENT
??為了使得輸出有所取分,使用不同顏色輸出:
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED); //未識別的符號為紅色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_BLUE);??? //關(guān)鍵字為藍色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);?? //運算符和分隔符為綠色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);?? //常量為黃色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY); //關(guān)鍵字為灰色
利用文件作為輸入。
輸入文檔1.txt:
double add(double x, double y)
{
??? double a = 3.456;
??? return x + y;
}
$
int main()
{
??????????? int i;
??????????? switch(i)
?????????? {
?????????????? case 1:?? break;
??????????? }
??????????? if(double(i,i)>i)
????????????????? break;
??????????? int a[10];
}?????????
輸出結(jié)果為:
其中注意紅色標記:
說明我們實現(xiàn)了錯誤識別的功能。
- 源代碼
#include <iostream>
#include <string>
#include <Windows.h>
using namespace std;
/* 單詞編碼 */
enum TokenCode
{
??? /*未定義*/
??? TK_UNDEF = 0,
??? /* 關(guān)鍵字 */
??? KW_VOID, //void關(guān)鍵字
??? KW_MAIN, //main關(guān)鍵字
??? KW_INT,????? //int關(guān)鍵字
??? KW_DOUBLE,?? //double關(guān)鍵字
??? KW_FOR,????? //for關(guān)鍵字
??? KW_WHILE,??? //while關(guān)鍵字
??? KW_SWITCH,?? //switch關(guān)鍵字
??? KW_CASE, //case關(guān)鍵字
??? KW_IF,?????? //if關(guān)鍵字
??? KW_ELSE, //else關(guān)鍵字
??? KW_RETURN,?? //return關(guān)鍵字
??? KW_BREAK,//break關(guān)鍵字
??? /* 運算符 */
??? TK_PLUS, //+加號
??? TK_MINUS,??? //-減號
??? TK_STAR, //*乘號
??? TK_DIVIDE,?? ///除號
??? TK_ASSIGN,?? //=賦值運算符
??? TK_EQ,?????? //==等于號
??? TK_LT,?????? //<小于號
??? TK_LEQ,????? //<=小于等于號
??? TK_GT,?????? //>大于號
??? TK_GEQ,????? //>=大于等于號
??? TK_MAO,??? //:冒號
??? /* 分隔符 */
??? TK_OPENPA,?? //(左圓括號
??? TK_CLOSEPA,? //)右圓括號
??? TK_OPENBR,?? //[左中括號
??? TK_CLOSEBR,? //]右中括號
??? TK_BEGIN,??? //{左大括號
??? TK_END,????? //}右大括號
??? TK_COMMA,??? //,逗號
??? TK_SEMOCOLOM, //;分號
??? /* 常量 */
??? TK_INT,????? //整型常量
??? TK_DOUBLE,?? //浮點型常量
??? /* 標識符 */
??? TK_IDENT
};
TokenCode code = TK_UNDEF;??? //記錄單詞的種別碼
const int MAX = 12;?????????????? //關(guān)鍵字數(shù)量
int row = 1;????????????????? //記錄字符所在的行數(shù)
string token = "";??????????????? //用于存儲單詞
char? keyWord[][10] = { "void","main","int","double","for","while","switch","case","if","else","return","break"};??? //存儲關(guān)鍵詞
void print(TokenCode code)
{
??? switch (code)
??? {
??????? /*未識別的符號*/
??? case TK_UNDEF:
??????? SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED); //未識別的符號為紅色
??????? cout << '(' << code << ',' << token << ")" << "未識別的符號在第" << row << "行。" << endl;
??????? return;
??????? break;
??????? /*關(guān)鍵字*/
??? case KW_VOID:
??? case KW_MAIN:
??? case KW_INT:????
??? case KW_DOUBLE:?
??? case KW_FOR:????
??? case KW_WHILE:??
??? case KW_SWITCH:?
??? case KW_CASE:
??? case KW_IF:?????
??? case KW_ELSE:
??? case KW_RETURN:?
??? case KW_BREAK:
??????? SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_BLUE);??? //關(guān)鍵字為藍色
??????? break;
??????? /* 運算符 */
??? case TK_PLUS:
??? case TK_MINUS:??
??? case TK_STAR:
??? case TK_DIVIDE:?
??? case TK_ASSIGN:?
??? case TK_EQ:?????
??? case TK_LT:?????
??? case TK_LEQ:
??? case TK_GT:?????
??? case TK_GEQ:????????
??? /* 分隔符 */
??? case TK_OPENPA:?
??? case TK_CLOSEPA:
??? case TK_OPENBR:?
??? case TK_CLOSEBR:
??? case TK_BEGIN:??
??? case TK_END:
??? case TK_COMMA:??
??? case TK_SEMOCOLOM:???
??? case TK_MAO:???
??????? SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);?? //運算符和分隔符為綠色
??????? break;
??????? /* 常量 */
??? case TK_INT:
??? case TK_DOUBLE:?
??????? SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);?? //常量為黃色
??????? if (token.find('.') == token.npos)
???????????? cout << '(' << code << ',' << atoi(token.c_str()) << ")" << endl;?????????????????????
??????? else
???????????? cout << '(' << code << ',' << atof(token.c_str()) << ")" << endl;?????????????????????????
??????? return;
??????? break;
??????? /* 標識符 */
??? case TK_IDENT:
??????? SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY); //關(guān)鍵字為灰色
??????? break;
??? default:
??????? break;
??? }
??? cout << '(' << code << ',' << token << ")" << endl;
}
bool isKey(string token)
{
??? for (int i = 0; i < MAX; i++)
??? {
??????? if (token.compare(keyWord[i]) == 0)
???????????? return true;
??? }
??? return false;
}
int? getKeyID(string token)
{
??? for (int i = 0; i < MAX; i++)
??? {?? //關(guān)鍵字的內(nèi)碼值為keyWord數(shù)組中對應的下標加1
??????? if (token.compare(keyWord[i]) == 0)
???????????? return i + 1;
??? }
??? return -1;
}
bool isLetter(char letter)
{
??? if ((letter >= 'a' && letter <= 'z') || (letter >= 'A' && letter <= 'Z'))
??????? return true;
??? return false;
}
bool isDigit(char digit)
{
??? if (digit >= '0' && digit <= '9')
??????? return true;
??? return false;
}
void lexicalAnalysis(FILE* fp1)
{
??? char ch;????????
??? while ((ch = fgetc(fp1)) != EOF)??
??? {
??????? token = ch;???????????????????????????????????
??????? if (ch == ' ' || ch == '\t' || ch == '\n')?
??????? {
???????????? if (ch == '\n')???????????????????????????
???????????????? row++;
???????????? continue;????????????????????????????????
??????? }
??????? else if (isLetter(ch))???????
??????? {
???????????? token = "";??????????????????
???????????? while (isLetter(ch) || isDigit(ch))
???????????? {
???????????????? token.push_back(ch);?
???????????????? ch = fgetc(fp1);?????
???????????? }
???????????? fseek(fp1, -1L, SEEK_CUR);
???????????? if (isKey(token))
???????????????? code = TokenCode(getKeyID(token));
???????????? else
???????????????? code = TK_IDENT;
??????? }
??????? else if (isDigit(ch))
??????? {
???????????? int isdouble = 0;
???????????? token = "";?????????
???????????? while (isDigit(ch))??
???????????? {
???????????????? token.push_back(ch);?????
???????????????? ch = fgetc(fp1);?????????????
????????????????
???????????????? if (ch == '.' && isdouble == 0)
???????????????? {
???????????????????? if (isDigit(fgetc(fp1)))
???????????????????? {
???????????????????????? isdouble = 1;???
???????????????????????? fseek(fp1, -1L, SEEK_CUR);???
???????????????????????? token.push_back(ch);?????????
???????????????????????? ch = fgetc(fp1);?????????????
???????????????????? }
???????????????? }
???????????? }
???????????? if (isdouble == 1)
???????????????? code = TK_DOUBLE;
???????????? else
???????????????? code = TK_INT;???????????????
???????????? fseek(fp1, -1L, SEEK_CUR);
??????? }
??????? else switch (ch)
??????? {
??????? case '+': code = TK_PLUS;?????????????????
???????????? break;
??????? case '-': code = TK_MINUS;???
???????????? break;
??????? case '*': code = TK_STAR;?????????????
???????????? break;
??????? case '/': code = TK_DIVIDE;???????
???????????? break;
??????? case '=':
??????? {
??????? ??? ch = fgetc(fp1);?????????????
???????????? if (ch == '=')???????????????
???????????? {
???????????????? token.push_back(ch);?
???????????????? code = TK_EQ;???????
???????????? }
???????????? else {???????????????????????
???????????????? code = TK_ASSIGN;????
???????????????? fseek(fp1, -1L, SEEK_CUR);
???????????? }
??????? }
??????? break;
??????? case '<':
??????? {
???????????? ch = fgetc(fp1);?????????????
???????????? if (ch == '=')???????????????
??????? ??? {
???????????????? token.push_back(ch);?
???????????????? code = TK_LEQ;???????????
???????????? }
???????????? else {???????????????????????
???????????????? code = TK_LT;???????
???????????????? fseek(fp1, -1L, SEEK_CUR);
???????????? }
??????? }
??????? break;
??????? case '>':
??????? {
???????????? ch = fgetc(fp1);?????????????
???????????? if (ch == '=')???????????????
???????????? {
???????????????? token.push_back(ch);?
???????????????? code = TK_GEQ;???????????
???????????? }
???????????? else {???????????????????????
???????????????? code = TK_GT;???????
???????????????? fseek(fp1, -1L, SEEK_CUR);
???????????? }
??????? }
??????? break;
???????
??????? case '(': code = TK_OPENPA;???????
???????????? break;
??????? case ')': code = TK_CLOSEPA;??
???????????? break;
??????? case '[': code = TK_OPENBR;???????
???????????? break;
??????? case ']': code = TK_CLOSEBR;??
???????????? break;
??????? case '{': code = TK_BEGIN;???
???????????? break;
??????? case '}': code = TK_END;?????
???????????? break;
??????? case ',': code = TK_COMMA;???
???????????? break;
??????? case ';': code = TK_SEMOCOLOM;
???????????? break;
??????? case':':code = TK_MAO;
???????????? break;
????????????
??????? default: code = TK_UNDEF;
??????? }
??????? print(code);????????????? //打印詞法分析結(jié)果
??? }
}
int main()
{
??? string filename;?????
??? FILE* fp1;???????????????
??? cout << "請輸入源文件名:" << endl;
??? while (true) {
??????? cin >> filename;?????
??????? if ((fopen_s(&fp1, filename.c_str(), "r")) == 0)????
???????????? break;
??????? else
???????????? cout << "路徑輸入錯誤!" << endl;?
??? }
??? cout << "/=***************************詞法分析結(jié)果***************************=/" << endl;
??? lexicalAnalysis(fp1);???? //詞法分析
??? fclose(fp1);
??? SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); //字體恢復原來的顏色
??? return 0;
}
- 實驗總結(jié)
這是編譯原理的第一次實驗,之前因為理解題意不明,也與老師探討了多次,上網(wǎng)查閱各種資料等等,最終終于完成了實驗。
在實驗過程中發(fā)生過種種問題,一開始為圖方便,想到過使用硬編碼來實現(xiàn)實驗,但最終還是使用了FA的方式。
通過本次實驗,我對NFA、DFA的知識有了更深入的了解,編程能力也進一步增強,可以說這次實驗是我對編譯原理與實際應用的第一次初步實現(xiàn),對我的影響深遠!