加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
lex.cpp 10.08 KB
一键复制 编辑 原始数据 按行查看 历史
周逸群 提交于 2022-04-27 09:43 . first and final commit
#include<stdio.h>
#include<cctype>
#include<algorithm>
#include<iostream>
#include<string>
#include <fstream>
#include<vector>
#include<map>
#include<string.h>
using namespace std;
/*
CQU-compiler-lab1
zhouyiqun 2022.4
本词法分析代码基于课程教材的状态机实现
*/
//!--- 输入输出文件 ---
//FILE *fpin=fopen(".\\testfiles\\6\\testfile1.txt", "r");
//ofstream fout(".\\testfiles\\6\\1_output.txt");
FILE *fpin;
ofstream fout;
//!--- 全局变量 ---
map<string, string> m;//用于检索保留字。只支持小写检索,检索函数进行了大小写转换
map<char, string> cm;//用于检索简单符号。简单符号:只需一个字符即可判断
int filesize;//源代码文件的大小
int state=0; //状态机
char c;//当前读入的字符
string token="";//正在处理的字符串
int mode;
//!--- 功能函数 ---
bool isDigit(char c){//判断数字
return c>='0'&& c<='9';
}
bool isLetter(char c){//判断英文字母
return c>='a'&& c<='z'||c>='A'&& c<='Z'||c=='_';//TODO ERROR line2 字符串无法识别
}
bool checkchar(char c){//检查字符常量的合法性
bool isOp= c=='+' || c=='-' || c=='*' || c=='/'|| c=='=';
bool isNum= isDigit(c);
bool isLt= isLetter(c);
return isOp|| isNum||isLt; //是运算符或数字或字母即合法
}
bool checkstr(char c){//检查字符串的合法性
return c==32 || c==33 ||(c>=35 && c<=126);//参照文法定义的要求
}
void init(){//初始化检索保留字、简单符号的map
m["const"]="CONSTTK";
m["int"]="INTTK";
m["char"]="CHARTK";
m["void"]="VOIDTK";
m["main"]="MAINTK";
m["if"]="IFTK";
m["else"]="ELSETK";
m["switch"]="SWITCHTK";
m["case"]="CASETK";
m["default"]="DEFAULTTK";
m["while"]="WHILETK";
m["for"]="FORTK";
m["scanf"]="SCANFTK";
m["printf"]="PRINTFTK";
m["return"]="RETURNTK";
cm['+']="PLUS";
cm['-']="MINU";
cm['*']="MULT";
cm['/']="DIV";
cm['(']="LPARENT";
cm[')']="RPARENT";
cm[';']="SEMICN";
cm[':']="COLON";
cm['[']="LBRACK";
cm[']']="RBRACK";
cm['{']="LBRACE";
cm['}']="RBRACE";
cm[',']="COMMA";
}
string keylookup(string s){//将字符串转为小写,并检索是否为保留字
for(int i=0; i<s.size(); i++){
s[i]= tolower(s[i]);
}
return m[s];
}
void get_nbc(){//跳过源文件中的空格 //TODO 用-1判断是否合理
while(int(c)==-1){
fpin+=1;
c= fgetc(fpin);
}
}
void normal_output(string cls, string word){ //正常情况下的输出,输出词+ 类别
if(mode==3 || mode==1){//mode=3: 全部输出, mode=1 关闭错误输出
fout << cls << " "<< word<< endl;
}
}
void normal_output(string cls, char word){ //正常情况, 函数重载
if(mode==3 || mode==1){//mode=3: 全部输出, mode=1 关闭错误输出
fout << cls << " "<< word<< endl;
}
}
void raise_error(int line_n, char e_code){//错误输出,输出行数+ 错误代码
cout << "*_An Error Occurs at line "<< line_n<< " _ "<< e_code<<"_*" << endl;
if(mode==2 || mode==3){//mode=3: 全部输出, mode=1 关闭错误输出
fout <<line_n+1<<" "<<e_code<< endl;//计数的行数比实际少1
}
}
//!--- 词法分析 ---
void lex_analyse(){
int linenum= 0;
do{//循环执行,直到文件位置标识符抵达文件尾
switch(state){
case 0://初始状态,根据下一个字符,决定跳转的状态(每个状态都是如此)
token="";//将字串清空
c=fgetc(fpin); //输入下一个字符,并将文件位置符后移
if(c=='\n'){//计算行号
linenum+=1;
}
if(int(c)==-1){//跳过空格
get_nbc();
}
if(isLetter(c)||c=='_'){//判断标识符/关键字开头
state= 1;
break;
}
else if(isDigit(c)) { //判断常数开头
state= 2;
break;
}
else if(cm[c]!=""){//判断简单符号
normal_output(cm[c], c);//简单符号可直接判断,将判断结果写入输出。
state= 0;//返回初始状态
break;
}
else{//判断复合符号
switch(c){
case '<':
state=3;
break;
case '>':
state=4;
break;
case '=':
state=5;
break;
case '!':
state=6;
break;
case '\'': //字符串常量
state=7;
break;
case '"': //字符串
state=8;
break;
default:
break;
}
}
break;
case 1: //识别标识符和关键字
token+=c;//将当前字符加入正在识别的字符串
c=fgetc(fpin);//读入下一个字符,并移动文件位置指示符
if(isDigit(c)||isLetter(c)||c=='_'){//除开头外,标识符可以由字母、数字、下划线组成。输入其他符号说明标识符已结束
state= 1;
break;
}
else{//下一个字符与标识符无关,标识符到此为止。进一步排除区别关键字/标识符
fseek(fpin, -1, SEEK_CUR);// 文件位置符后退一位,消除fgetc的影响,使得新的字符能够在初始状态被重新处理。需用fseek操作FILE对象的文件位置符
state=0;
if(keylookup(token)!=""){ //写入关键字
normal_output(keylookup(token), token);
}else{//写入标识符
normal_output("IDENFR ", token);
}
}
break;
case 2://识别整数常数
token+=c;
c=fgetc(fpin);
if(isDigit(c)){//输入非数字说明数字已结束
state= 2;
break;
}
else{ //下一个字符与整数常量无关,整数常量到此为止。
fseek(fpin, -1, SEEK_CUR);
state= 0;
normal_output( "INTCON ",token);//写入整数常量
}
break;
case 3://小于号
token+=c;
c=fgetc(fpin);
if(c=='='){//下一个字符为等号,识别为LEQ
token+=c;//将等号插入当前识别的字符串,以便输出
normal_output("LEQ " ,token);
state= 0;//当前字符串识别完毕,接下来回到初始状态
break;
}
else{//下一个不为等号,识别为LSS
fseek(fpin, -1, SEEK_CUR);//位置符回退
state= 0;//接下来返回初始状态
normal_output("LSS ",token);
break;
}
break;
case 4://大于号
token+=c;
c=fgetc(fpin);
if(c=='='){//下一个字符为等号,识别为GEQ
token+=c;
normal_output("GEQ ",token);
state= 0;
break;
}
else{//下一个不为等号,识别为GRE
fseek(fpin, -1, SEEK_CUR);
state= 0;
normal_output("GRE ",token);
break;
}
break;
case 5://等号
token+=c;
c=fgetc(fpin);
if(c=='='){//下一个字符为等号,识别为EQL
token+=c;
normal_output("EQL ",token);
state= 0;
break;
}
else{//下一个字符不为等号,识别为ASSIGN
fseek(fpin, -1, SEEK_CUR);
state= 0;
normal_output("ASSIGN ",token);
break;
}
break;
case 6://感叹号!
token+=c;
c=fgetc(fpin);
if(c=='='){//下一个字符为=,识别为NEQ
token+=c;
normal_output("NEQ ",token);
break;
}
else{//下一个字符不为=,出错
fseek(fpin, -1, SEEK_CUR);
state= 0;
raise_error(linenum,'a');//$ 感叹后号后不为等号,a类错误
break;
}
break;
case 7://单引号'(字符常量的左引号)
c=fgetc(fpin);
if(checkchar(c)){//输入合法字符
token+=c;
state=9;
break;
}
else{//输入非法字符,包括单引号为空的情况
//cout << "wrong char for const"<< endl;
raise_error(linenum, 'a');//$ 字符常量中字符为空或非法,a类错误
c=fgetc(fpin);
if(c=='\''){//若后面还有一个单引号
state=0;
}else{
fseek(fpin, -1, SEEK_CUR);
state=0;
}
}
break;
case 8://双引号" (字符串的左引号)
c=fgetc(fpin);
if(checkstr(c)){//合法的字符串字符
token+= c;
state=10;
break;
}
else{//非法的字符串字符,包括了双引号(因为不能为空)
// cout <<"illegal char for str"<<endl;
raise_error(linenum, 'a');//$ 字符串常量中字符为空或非法,a类错误
c=fgetc(fpin);
if(c=='\"'){//若后面还有一个双引号
state=0;
}else{//回退
fseek(fpin, -1, SEEK_CUR);
state=0;
}
}
break;
case 9://字符常量,读入了一个字符
c=fgetc(fpin);
if(c=='\''){//下一个字符为',即字符常量的右引号,字符常量结束 、
normal_output("CHARCON ",token);
state=0;
break;
}
else{//输入的不是右单引号,超过一个字符被赋给字符常量
// cout <<"more than one char"<<endl;
state= 0;
break;
}
case 10://字符串常量,读入了一个字符
c=fgetc(fpin);
if(c=='"'){//下一个字符是右边的双引号,终止
normal_output("STRCON ",token);
state= 0;
break;
}
else{
if(checkstr(c)){//下一个字符仍然为合法字符串字符,继续回到state 10
token+= c;
state= 10;
break;
}
else{//非法符号,报错
c=fgetc(fpin);
if(c=='\"'){//若后面还有一个双引号
state=0;
}else{//回退
fseek(fpin, -1, SEEK_CUR);
state=0;
}
raise_error(linenum, 'a');//$ 字符串常量非法字符,a类错误
}
}
break;
default://状态不存在,输出错误
fout << "NO CASE ERROR"<< endl;
return;
break;
}
}while(ftell(fpin)!=filesize);
}
int lex_top(string inFileName, string outFileName, int md){
//mode 1:close error output; mode 2: close lex output; mode 3: open all output
mode= 2;
cout << "*_Analysis Mode=_"<<mode<<"_*"<<endl;
//读取文件
const char* inFileName_c= inFileName.c_str();
fpin=fopen(inFileName_c, "r"); //打开输入文件
fout.open(outFileName);//打开输出文件
cout << "*_Analysis running at_"<<inFileName<<"_*"<<endl;
cout << "*_Lex analysis start_*"<<endl;
fseek(fpin,0,SEEK_END); //移动指针到文件末尾,计算文件大小
filesize = ftell(fpin);
cout << "*_Filesize="<< filesize<<"_*" << endl;
rewind(fpin);//将指针移动到开头
init();//初始化关键字表、简单符号表
//运行词法分析
lex_analyse(); //进行词法分析
cout << "*_Lex analysis done_*"<<endl;
//关闭文件
fclose(fpin);
fout.close();
return 0;
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化