一、实验目的
1. 理解词法分析器的基本功能
2. 能够编写简单的词法分析器
二、实验内容
1. 测试 toy.cpp
a) 编译 clang++ toy.cpp -o toy
b) 运行 ./toy 或者 cat example.txt | ./toy 或者 ./toy < example.txt
2. 扩展功能
a) 输出词素(lexeme)
b) 识别注释 ‘#’开头直至行尾
c) 识别注释 /* ..... */
d) 识别出正确的小数,例如 12.34 若格式错误,则报错,例如 12.34.56
e) 识别出十六进制数(0x 开头)和八进制数(0 开头),转换为十进制数,输出
★f) 识别出指数形式的常量,转换为小数形式,输出 例如: 2.5e3 被转换为 2500 3.7e-2 被转换为 0.037
★g) 增加符号表,将识别出的标识符(identifier)加入符号表,记录词素、标识符所在的行号
本文章只涉及前五小问。
该实验使用C++编程语言实现功能。第一小问利用C++的cout来输出词素;第二小问利用if判断语句判断输入字符流是否含有”#”来判断该句是否为#类注释;第三小问对于”/*…*/”类注释,首先设置一个自增int类型数据cnt=0,判断第一个输入字符是否为’/’,再判断第二个字符是否为’*’,该过程中如果为真则cnt自增1,若此时cnt为2则该句可能为只有一个’/*’的错误注释,需对后面语句进行判定,若后面出现连续的’*/’则为真注释,否则为假注释;第四小问利用对数字串中的小数点数量进行判断,如果小数点数量大于1则报错;第五小问对十六进制数和八进制数的判定与转化首先判断第一个字符是否为’0’,如果是则可能为八进制数或十六进制数,如果0后面为’ ’或’\n’则该数为0,否则判定下一个字符是否为’x’,是则为16进制数,否则为8进制数。
#include<bits/stdc++.h>
#include <stdlib.h>
#include <cctype>
#include <cstdio>
#include <string>
using namespace std;
char asc[15]= {'!','"','#','$','%','&',',','(',')','*','+',',','-','.','/'}; // !的asc吗为33(x-33)
char asc2[7]= {':',';','<','=','>','?','@'}; // :的asc吗为58(x-58)
int cnt=0,pointnum=0;// /*表示数和字符串目前长度
string s="",sixte="",eigh="";
bool cwzhushi=false,bef=false;
//===----------------------------------------------------------------------===//
// Lexer
//===----------------------------------------------------------------------===//
// The lexer returns tokens [0-255] if it is an unknown character, otherwise one
// of these for known things.
int fastpow(int a,int n)
{
int ans=1;
while(n)
{
if(n&1)
ans*=a;
a*=a;
n>>=1;
}
return ans;
}
enum Token
{
tok_eof = -1,
// commands
tok_def = -2,
tok_extern = -3,
// primary---标识符(identifier)
tok_identifier = -4,
tok_number = -5,
};
static std::string IdentifierStr; // Filled in if tok_identifier
static double NumVal; // Filled in if tok_number
/// gettok - Return the next token from standard input.
static int gettok()
{
static int LastChar = ' ';
// Skip any whitespace.
if(LastChar == '\n'&&cnt==2)
{
cwzhushi=true;///判断是否为错误注释
}
if(cnt==4)
{
cwzhushi=false;
}
//cout<<"cnt=="<<cnt<<endl;
if(cwzhushi&&(cnt==2||cnt==3))
{
//cout<<s<<"(这是s)"<<endl;
if(s[0]=='/'&&s[1]=='*')
{
cout<<"该句为错误注释"<<endl;
s="";
cnt=0;
cwzhushi=false;
}
}
while (isspace(LastChar))
{
LastChar = getchar();
}
if(cnt==2)
{
//cout<<"---------------------该语句可能为注释或错误注释,故在此进行提醒,如果最后未显示为注释则为错误注释---------------------"<<endl;
cout<<"---------------------该语句可能为注释或错误注释,故在此进行提醒---------------------"<<endl;
}
if (isalpha(LastChar)) // identifier: [a-zA-Z][a-zA-Z0-9]*
{
pointnum=0;
IdentifierStr = LastChar;
while (isalnum((LastChar = getchar())))
IdentifierStr += LastChar;
cout<<"这个是IdentifierStr中的词素: "<<IdentifierStr<<endl;
if (IdentifierStr == "def")
return tok_def;//-2
if (IdentifierStr == "extern")
return tok_extern;//-3
return tok_identifier;//-4
}
if (isdigit(LastChar) || LastChar == '.') // Number: [0-9.]+,数字
{
std::string NumStr;
bool not10=false;
int pointcnt=0;
do
{
NumStr += LastChar;
LastChar = getchar();
if(LastChar == '.')
pointcnt++;
pointnum++;
//cout<<"NumStr是"<<NumStr<<endl;
if(NumStr[0]=='0')///如果一组数据中第一个数字为0则该串数字可能为16进制数或8进制数
{
not10=true;
if(LastChar =='x')///计算16进制转10进制
{
NumStr += LastChar;
cout<<"16进制数标志"<<NumStr<<endl;
do
{
LastChar = getchar();
sixte+=LastChar;
}
while (isdigit(LastChar) || LastChar == '.'||(LastChar >='a'&&LastChar <='f'));
cout<<"16进制数位"<<sixte<<endl;
double si=0;int Point=-1;
for(int j=0;j<sixte.size();j++)
{
if(sixte[j]== '.')
{
Point=j;
break;
}
}
if(Point!=-1)
{
for(int j=0;j<Point;j++)
{
if(sixte[j]>='0'&&sixte[j]<='9')
{
double si1=int(sixte[j])-48;
si+=(si1*fastpow(16,Point-1-j));
}
if(sixte[j] >='a'&&sixte[j] <='f')
{
double si1=int(sixte[j])-87;
si+=(si1*fastpow(16,Point-1-j));
}
}
for(int j=Point+1;j<sixte.size();j++)
{
if(sixte[j]>='0'&&sixte[j]<='9')
{
double si1=int(sixte[j])-48;
si+=(si1*1.0/fastpow(16,j-Point));
//cout<<j-Point<<" "<<fastpow(16,j-Point)<<endl;
}
if(sixte[j] >='a'&&sixte[j] <='f')
{
double si1=int(sixte[j])-87;
si+=(si1*1.0/fastpow(16,j-Point));
}
}
}
else
{
//cout<<"nopoint"<<endl;
for(int j=0;j<sixte.size();j++)
{
if(sixte[j]>='0'&&sixte[j]<='9')
{
double si1=int(sixte[j])-48;
si+=(si1*fastpow(16,sixte.size()-2-j));
//cout<<" "<<fastpow(16,sixte.size()-2-j)<<endl;
}
if(sixte[j] >='a'&&sixte[j] <='f')
{
double si1=int(sixte[j])-87;
si+=(si1*fastpow(16,sixte.size()-2-j));
//cout<<si<<endl;
}
}
}
s="";
cout<<"16进制数转十进制结果为"<<si<<endl;
sixte="";
}//----------------------------------------------------------------
else///计算8进制转10进制---也有可能就是0
{
do
{
if(LastChar==32||LastChar==10)///0的后面是' '或者\n
{
cout<<"这个是IdentifierStr中的词素(数字): 0"<<endl;
return tok_number;
}
eigh+=LastChar;
//cout<<"LastChar的值为"<<LastChar<<endl;
LastChar = getchar();
}
while (isdigit(LastChar) || LastChar == '.');
cout<<"8进制数标志"<<NumStr<<endl;
cout<<"8进制数位"<<eigh<<endl;
double si=0;int Point=-1;
for(int j=0; j<eigh.size(); j++)
{
if(eigh[j]== '.')
{
Point=j;
break;
}
}
if(Point!=-1)
{
for(int j=0; j<Point; j++)
{
if(eigh[j]>='0'&&eigh[j]<='9')
{
double si1=int(eigh[j])-48;
si+=(si1*fastpow(8,Point-1-j));
}
if(eigh[j] >='a'&&eigh[j] <='f')
{
double si1=int(eigh[j])-87;
si+=(si1*fastpow(8,Point-1-j));
}
}
for(int j=Point+1; j<eigh.size(); j++)
{
if(eigh[j]>='0'&&eigh[j]<='9')
{
double si1=int(eigh[j])-48;
si+=(si1*1.0/fastpow(8,j-Point));
//cout<<j-Point<<" "<<fastpow(16,j-Point)<<endl;
}
if(eigh[j] >='a'&&eigh[j] <='f')
{
double si1=int(eigh[j])-87;
si+=(si1*1.0/fastpow(8,j-Point));
}
}
}
else
{
//cout<<"nopoint"<<endl;
for(int j=0; j<eigh.size(); j++)
{
if(eigh[j]>='0'&&eigh[j]<='9')
{
double si1=int(eigh[j])-48;
si+=(si1*fastpow(8,eigh.size()-1-j));
//cout<<eigh.size()-2-j<<" "<<fastpow(8,eigh.size()-1-j)<<endl;
}
if(eigh[j] >='a'&&eigh[j] <='f')
{
double si1=int(eigh[j])-87;
si+=(si1*fastpow(8,eigh.size()-1-j));
//cout<<si<<endl;
}
}
}
s="";
cout<<"8进制数转十进制结果为"<<si<<endl;
eigh="";
}
}
}
while (isdigit(LastChar) || LastChar == '.');
if(!not10)///如果是十进制数字(该代码意义为如果不是非十进制数)
{
NumVal = strtod(NumStr.c_str(), 0);
s="";
s+=LastChar;
if(pointcnt<=1)///判断这串数字有多少小数点
cout<<"这个是IdentifierStr中的词素(数字): "<<NumVal<<endl;
else
cout<<"该数字格式有误,有误数字为"<<NumStr<<endl;
cout<<"该数长为"<<pointnum<<endl;
pointnum=0;
}
return tok_number;
}
if (LastChar == '#')//#为开头的注释
{
pointnum=0;
// Comment until end of line.
string s1="";
do
{
LastChar = getchar();
s1+=LastChar;
}
while (LastChar != EOF && LastChar != '\n' && LastChar != '\r');
cout<<"这段以#打头的语句--"<<"#"<<s1<<"是注释"<<endl;
if (LastChar != EOF)
return gettok();
}
// Check for end of file. Don't eat the EOF.
if (LastChar == EOF)
return tok_eof;//-1
// Otherwise, just return the character as its ascii value.
int ThisChar = LastChar;
if(ThisChar>=33&&ThisChar<=47)
{
pointnum=0;
if(ThisChar==47&&cnt==0)//判断第一个字符是否为'/'
{
s="";
cout<<"这个可能是IdentifierStr中的词素: "<<asc[ThisChar-33]<<endl;
cnt++;
bef=false;
}
else if(ThisChar==42&&cnt==1)//判断第二个字符是否为'*'
{
cout<<"这个可能是IdentifierStr中的词素: "<<asc[ThisChar-33]<<endl;
cnt++;
}
else if(ThisChar==42&&cnt==2)
{
cout<<"这个可能是IdentifierStr中的词素: "<<asc[ThisChar-33]<<endl;
cnt++;
bef=true;
}
else if(ThisChar==47&&cnt==3&&bef)
{
cout<<"这个可能是IdentifierStr中的词素: "<<asc[ThisChar-33]<<endl;
cnt++;
}
else if(cnt<2)
cout<<"这个是IdentifierStr中的词素: "<<asc[ThisChar-33]<<endl;
else
cout<<"这个可能是IdentifierStr中的词素: "<<asc[ThisChar-33]<<endl;
}
if(ThisChar>=58&&ThisChar<=64)
{
pointnum=0;
cout<<"这个是IdentifierStr中的词素: "<<asc2[ThisChar-58]<<endl;
}
//cout<<"这是cnt"<<cnt<<endl;
s+=LastChar;
//cout<<"这是s的:"<<s<<endl;
LastChar = getchar();
return ThisChar;
}
//--------------------
int main()
{
int tok;
bool cwzhushi=false;
fprintf(stderr, "ready> ");
do
{
if(cnt==4&&!cwzhushi)
{
if(s[0]=='/'&&s[1]=='*')
{
//cout<<"yes1"<<endl;
if(s[s.size()-1]=='/'&&s[s.size()-2]=='*')
{
//system("cls");
cout<<"该句为注释"<<endl;
}
}
}
if(cnt==4)
{
s="";
cnt=0;
}
tok = gettok();
fprintf(stderr, "got token: %d\n",tok);
cout<<endl;
}
while(tok != tok_eof);
return 0;
}
输出截图: