# 词法分析器在程序编译过程中所处的位置
# 词法分析器
Lexical Analyzer
,输入源程序,扫描器 Scanner
从左至右逐个字符地对源程序进行扫描,产生多个单词符号;单词符号的种类有
- 基本字(关键字):
begin
、repeat
、for
等程序语言定义好的 - 标识符:用来表示各种名字,如变量名、数组名和过程名
- 常数:各种类型的常数
- 运算符:
+、-、*、/
- 界符:
,、;、()、space符
输出的单词符号的表示形式为 (单词种别,单词自身的值)
,单词种别通常用整数编码表示
- 若一个种别只有一个单词符号,则种别编码就代表该单词符号,假定基本字、运算符、界符都是一符一种。
- 若一个种别有多个单词符号,则对于每个单词符号,给出种别编码和自身的值
- 标识符单列一种,标识符自身的值表示成按机器字节划分的内部码。
- 常数按类型分种,常数的值则表示成标准的二进制形式
# 如何设计
# 扫描缓冲区
两个指针分别指向起点和搜索位置,考虑到单词长度超过缓冲区的长度,造成存储不连续,可以分为两个半区互补使用。
如果某单词的结尾在一个半区找不到,那么在下一个半区一定能找得到,所以半区的长度就是程序语言允许的单词最大长度,比如某编程语言的标识符长度不超过 128
,就可以推断出编译器的扫描缓冲区总长度为 256
。
# 状态转换图
状态转换图是一张有限方向图,节点代表状态,用圆圈表示,状态之间用箭弧连接,上面的标记(字符)代表射出的结点状态下可能出现的输入字符或字符类,一张转换图只包含有限个状态,其中有一个为初态,至少有一个终态。
状态转换图可用于识别一定的字符串,若存在一条从初态到某一终态的道路,且这条路上所有弧上的标记符连接成的字等于 α
,则称 α
被该状态转换图所识别。
终态上的 *
代表最后一个输入的字符不属于刚才读入的单词,将其退回去。
几点限制,不必使用超前搜索
- 所有基本字都是保留字,用户不能用它们作自己的标识符
- 基本字作为特殊的标识符来处理,使用保留字表
- 如果基本字、标识符和常数(或括号)之间没有确定的运算符或界符作为间隔,则必须使用一个空白符作间隔
#include<bits/stdc++.h> | |
using namespace std; | |
const int max_word = 505; | |
char token[12]; | |
char in[105]; | |
FILE *fin,*fout; | |
int cnt = 0,token_num = 0; | |
int row = 1; | |
int flag = 0; | |
char ch; | |
char datain[255]; | |
// 关键字 | |
const char keyWord[13][20] = {"main","if","else","do","while","for","switch", | |
"case","int","double","float","long","void" | |
}; | |
void init_token(); | |
void getbc(); | |
void m_getch(); | |
void concat(); | |
int digit(); | |
int letter(); | |
int reserve(); | |
int scanner(); | |
int main() | |
{ | |
int temp; | |
char input[30]; | |
cout<<"请输入源文件名:"; | |
for(;;) { | |
cin>>input; | |
if((fin = freopen(input,"r",stdin))!=NULL){ | |
cout << "从 "<<input<<" 读取"<<endl; | |
while((scanf("%[^`]s",&datain))!=EOF) | |
printf(datain); | |
break; | |
} | |
else | |
cout<<"路径输入有误"<<endl; | |
} | |
freopen(input,"r",stdin); | |
while(1) { | |
temp = scanner(); | |
if(temp==-1) { | |
break; | |
} | |
switch(temp) { | |
case -10: | |
cout<<"第 "<<row<<" 行 识别有误."<<endl; | |
break; | |
default: | |
cout<<"第 "<<row<<" 行 <"<<temp<<","<<token<<">"<<endl; | |
break; | |
} | |
} | |
return 0; | |
} | |
void init_token() | |
{ | |
int i; | |
for(i = 0; i < 12; i++) { | |
token[i] = 0; | |
} | |
} | |
void getbc() | |
{ | |
while(ch == ' ' || ch == '\t' || ch == '\n') { | |
if(ch == '\n') { | |
row++;// 顺便记录行数 | |
} | |
ch=getc(fin); | |
} | |
} | |
void m_getch() | |
{ | |
if(flag == 0) { | |
ch = getc(fin); | |
} | |
} | |
void concat() | |
{ | |
token[token_num++] = ch; | |
ch = getc(fin); | |
} | |
int digit() | |
{ | |
if (ch >= '0'&&ch <= '9') | |
return 1; | |
else | |
return 0; | |
} | |
int letter() | |
{ | |
if (ch >= 'a'&&ch <= 'z' || ch >= 'A'&&ch <= 'Z') | |
return 1; | |
else | |
return 0; | |
} | |
int reserve() | |
{ | |
for(int i = 0; i <13; i++) { | |
if(strcmp(token,keyWord[i]) == 0) { | |
//3 为关键词 | |
return 3; | |
} | |
} | |
//2 为标识符 | |
return 2; | |
} | |
int scanner() | |
{ | |
init_token(); | |
m_getch(); | |
flag = 1; | |
getbc(); | |
token_num = 0; | |
if(letter()) { | |
while(letter()||digit()) { | |
concat(); | |
} | |
token[token_num++] = '\0'; | |
reserve(); | |
} | |
// 是数字 | |
else if(digit()) { | |
while(digit() || ch == '.') { | |
concat(); | |
} | |
return 1; | |
} else { | |
token[token_num++] = ch; | |
switch(ch) { | |
case '(': | |
ch = getc(fin); | |
return 16; | |
case ')': | |
ch = getc(fin); | |
return 17; | |
case '{': | |
ch = getc(fin); | |
return 33; | |
case '}': | |
ch = getc(fin); | |
return 34; | |
case '+': | |
ch = getc(fin); | |
if(ch == '+') { | |
token[token_num++] = ch; | |
ch = getc(fin); | |
return 29; | |
} else { | |
return 18; | |
} | |
case '-': | |
ch = getc(fin); | |
if(ch == '-') { | |
concat(); | |
return 30; | |
} else { | |
return 19; | |
} | |
case '*': | |
ch = getc(fin); | |
if(ch == '/') { | |
concat(); | |
return 32; | |
} else { | |
return 20; | |
} | |
case '/': | |
ch = getc(fin); | |
if(ch == '*') { | |
concat(); | |
return 31; | |
} else { | |
return 21; | |
} | |
// 这里要重新编码 | |
case '=': | |
ch = getc(fin); | |
if(ch == '=') { | |
concat(); | |
return 23; | |
} else { | |
return 22; | |
} | |
case '>': | |
ch = getc(fin); | |
if(ch == '=') { | |
concat(); | |
return 24; | |
} else { | |
return 23; | |
} | |
case '<': | |
ch = getc(fin); | |
if(ch == '=') { | |
concat(); | |
return 26; | |
} else { | |
return 25; | |
} | |
case ';': | |
ch = getc(fin); | |
return 27; | |
case '"': | |
ch = getc(fin); | |
return 28; | |
case '!': | |
ch = getc(fin); | |
if(ch == '=') { | |
concat(); | |
return 37; | |
} else { | |
return 36; | |
} | |
case '#': | |
ch = getc(fin); | |
return -2; | |
case ',': | |
ch = getc(fin); | |
return 35; | |
case EOF: | |
return -1; | |
default: | |
ch = getc(fin); | |
return -10; | |
} | |
} | |
} |
示例 data.txt
int main() | |
{ | |
int b=10;float f; | |
f+=b; | |
return 0; | |
} |