未备案网站,中国进口贸易网官网,网页设计随机点名代码,携程做旅游的网站源代码及附件#xff1a;编译原理实验一源程序及附件资源-CSDN文库实验题目 实验要求 实验设计
前两部分指出了实验的宏观把控#xff0c;为了具体实施实验#xff0c;我们需要预先为实验做出如下设计#xff1a;
本次实验我选取了C语言的一个子集进行设计词法分析器编译原理实验一源程序及附件资源-CSDN文库实验题目 实验要求 实验设计
前两部分指出了实验的宏观把控为了具体实施实验我们需要预先为实验做出如下设计
本次实验我选取了C语言的一个子集进行设计词法分析器其中单词种类如下也可参考附件中的单词种类表 根据题目要求我们用正则文法来定义我们的子集
S→关键字|运算符|分界符|整型常量|浮点型常量|标识符关键字
→ void|main|int|float|for|while|switch|case|if|else|return|break
3运算符 → |-|*|/||||||
4分界符 → (|)|[|]|{|}|,|;|:
5整型常量 → digit(digit)*
6浮点型常量 → digit(digit).digit(digit)
7digit→ 0|1|2|3|4|5|6|7|8|9
8letter→ a|b|…|z|A|B|…|Z
9标识符 → letter(letter|digit)*
实验分析
基本算法思想是读取文件逐个字符分析若是空白符则跳过为字母时将连续的字母使用超前搜索组合成为变量或关键字若是数字则要判断是否为浮点数即利用超前搜索判断扫描到的字符是否为小数点若是分隔符或者操作符利用switch语句判断并输出若是其他字符输出为未定义的字符。
相关代码段
void lexicalAnalysis(FILE* fp)
{ char ch; while ((ch fgetc(fp)) ! EOF) / { token ch; if (ch || ch \t || ch \n) //忽略空格、Tab和回车 { if (ch \n) //遇到换行符记录行数的row加1 row; continue; } else if (isLetter(ch)) //以字母开头关键字或标识符 { token ; //token初始化 while (isLetter(ch) || isDigit(ch)) //非字母或数字时退出将单词存储在token中 { token.push_back(ch); //将读取的字符ch存入token中 ch fgetc(fp); //获取下一个字符 } //文件指针后退一个字节即重新读取上述单词后的第一个字符 fseek(fp, -1L, SEEK_CUR); if (isKey(token)) //关键字 code TokenCode(getKeyID(token)); else //标识符 code TK_IDENT; //单词为标识符 } else if (isDigit(ch)) //无符号常数以数字开头 { int isdouble 0; //标记是否为浮点数 token ; //token初始化 while (isDigit(ch)) //当前获取到的字符为数字 { token.push_back(ch); //读取数字将其存入token中 ch fgetc(fp); //从文件中获取下一个字符 //该单词中第一次出现小数点 if (ch . isdouble 0) { //小数点下一位是数字 if (isDigit(fgetc(fp))) { isdouble 1; //标记该常数中已经出现过小数点 fseek(fp, -1L, SEEK_CUR); //将超前读取的小数点后一位重新读取 token.push_back(ch); //将小数点入token中 ch fgetc(fp); //读取小数点后的下一位数字 } } } if (isdouble 1) code TK_DOUBLE; //单词为浮点型 else code TK_INT; //单词为整型 //文件指针后退一个字节即重新读取常数后的第一个字符 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); //打印词法分析结果 }
}
定义了如下数据结构作为状态终态 /* 关键字 */ KW_VOID, //void关键字 KW_MAIN, //main关键字 KW_INT, //int关键字 KW_DOUBLE, //double关键字 KW_FOR, //for关键字 KW_WHILE, //while关键字 KW_SWITCH, //switch关键字 KW_CASE, //case关键字 KW_IF, //if关键字 KW_ELSE, //else关键字 KW_RETURN, //return关键字 KW_BREAK,//break关键字 /* 运算符 */ 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); //关键字为蓝色
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); //关键字为灰色
利用文件作为输入。
输入文档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];
}
输出结果为 其中注意红色标记 说明我们实现了错误识别的功能。
源代码
#include iostream
#include string
#include Windows.h
using namespace std; /* 单词编码 */
enum TokenCode
{ /*未定义*/ TK_UNDEF 0, /* 关键字 */ KW_VOID, //void关键字 KW_MAIN, //main关键字 KW_INT, //int关键字 KW_DOUBLE, //double关键字 KW_FOR, //for关键字 KW_WHILE, //while关键字 KW_SWITCH, //switch关键字 KW_CASE, //case关键字 KW_IF, //if关键字 KW_ELSE, //else关键字 KW_RETURN, //return关键字 KW_BREAK,//break关键字 /* 运算符 */ 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; //关键字数量
int row 1; //记录字符所在的行数
string token ; //用于存储单词
char keyWord[][10] { void,main,int,double,for,while,switch,case,if,else,return,break}; //存储关键词
void print(TokenCode code)
{ switch (code) { /*未识别的符号*/ case TK_UNDEF: SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED); //未识别的符号为红色 cout ( code , token ) 未识别的符号在第 row 行。 endl; return; break; /*关键字*/ 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); //关键字为蓝色 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); //关键字为灰色 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) { //关键字的内码值为keyWord数组中对应的下标加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); //打印词法分析结果 }
} 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 /***************************词法分析结果***************************/ endl; lexicalAnalysis(fp1); //词法分析 fclose(fp1); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); //字体恢复原来的颜色 return 0;
}
实验总结
这是编译原理的第一次实验之前因为理解题意不明也与老师探讨了多次上网查阅各种资料等等最终终于完成了实验。
在实验过程中发生过种种问题一开始为图方便想到过使用硬编码来实现实验但最终还是使用了FA的方式。
通过本次实验我对NFA、DFA的知识有了更深入的了解编程能力也进一步增强可以说这次实验是我对编译原理与实际应用的第一次初步实现对我的影响深远