C语言实现简单的词法分析器
词法分析
词法分析是编译的第一个阶段,其任务是:从左至右对原程序进行扫描,产生一个个单词符号。
C语言子集的单词符号表
对常用有限个单词进行编码
(1)identify_char:标识符
(2)include等:关键词
(3)+ - * /:操作符
(4){ }等:分界符
(5)# 等:程序不能识别的字符
注:本段程序只能分析出下表给出的字符
词法分析的状态转换图如下
根据词法分析器的状态装换图可以设计词法分析程序的框架:
(1)判断是否为关键字,字母,数字。
bool isKey(char * token);//keyword
bool isLetter(char letter);//letter
bool isDigit(char digit);//digit
(2)通过代码访问文件,文件指针依次扫描文件中的字符,如果文件指针遇到(\t,\n,’ '),程序什么都不做,将得到的字符串进行判断,文件指针向后退一格,准备读下一个字符串。
(3)对使用到的文件函数进行说明
//1.
/*从文件指针fgetc(stream)指向的文件读取一个字符,读取一个字节后,光标位置后移一个字节
* format: int fgetc(FILE*stream)
* 函数返回得到的字符值
* 读到结尾或者读到错误,返回EOF
* EOF是一个有效的整型值
* 二进制文件使用feof函数进行结束检测
* 用ferror函数进行出错检查
//2.
* /*int fseek(FILE*stream,long offset,int fromwhere),调整文件当前指针
* 若果函数成功执行,stream指针将指向以fromwhere为基准,偏移offset个字节的位置,函数返回0
* 若果函数执行失败(offset的值大于等于2*1024*1024*1024,即long的整数范围为2G),函数返回非零值
* 偏移起始位置:文件头0(SEEK_SET)
* 当前位置:当前位置1(SEEK_CUR)
* 文件尾2(SEEK_END)*/
*/
(4)详细注释在代码在代码注释中。
测试文件:
词法分析程序源代码
编译环境:eclipse + MinGW
#include<stdio.h>
#include<string.h>
#define bool int
#define true 1
#define false 0
#define MAX 11
char ch = ' ';
char* keyWord[11] = {"include","main","void","break","int","float","if","else","while","printf","for"};
char token[20];//定义获取的字符
//判断是否是关键字
bool isKey(char * token)
{
for(int i = 0;i < MAX;i++)
{
if(strcmp(token,keyWord[i]) == 0)
return true;
}
return false;
}
//判断是否是字母
bool isLetter(char letter)
{
if((letter >= 'a' && letter <= 'z')||(letter >= 'A' && letter <= 'Z'))
return true;
else
return false;
}
//判断是否是数字
bool isDigit(char digit)
{
if(digit >= '0' && digit <= '9')
return true;
else
return false;
}
//1.
/*从文件指针fgetc(stream)指向的文件读取一个字符,读取一个字节后,光标位置后移一个字节
* format: int fgetc(FILE*stream)
* 函数返回得到的字符值
* 读到结尾或者读到错误,返回EOF
* EOF是一个有效的整型值
* 二进制文件使用feof函数进行结束检测
* 用ferror函数进行出错检查
*/
void analyze(FILE *fpin)
{
while((ch = fgetc(fpin)) != EOF){
if(ch == ' '||ch == '\t'||ch == '\n'){}//读到空字符,空格,换行符什么都不做
else if(isLetter(ch)){
char token[20]={'\0'};
int i=0;
while(isLetter(ch)||isDigit(ch)){
token[i] = ch;
i++;
ch = fgetc(fpin);//读下一个字符
}
//回退一个指针
/*int fseek(FILE*stream,long offset,int fromwhere),调整文件当前指针
* 若果函数成功执行,stream指针将指向以fromwhere为基准,偏移offset个字节的位置,函数返回0
* 若果函数执行失败(offset的值大于等于2*1024*1024*1024,即long的整数范围为2G),函数返回非零值*
* 偏移起始位置:文件头0(SEEK_SET)
* 当前位置:当前位置1(SEEK_CUR)
* 文件尾2(SEEK_END)*/
fseek(fpin,-1L,SEEK_CUR);//文件指针停留在空字符,空格,换行符的位置上
if(isKey(token)){
//关键字
int i,j;
for( i = 0;i < MAX;i++)
{
if(strcmp(token,keyWord[i]) == 0)j=i;
} printf("%s\t%d\tkeyword\n",token,j);
}
else{
//标识符
printf("%s\t4\tidentify_char\n",token);
}
}
else if(isDigit(ch)||(ch == '.'))
{
int i=0;
char token[20]={'\0'};//清空缓冲数组;但文件指针还在;即读入下一个字符
while(isDigit(ch)||(ch == '.'&&isDigit(fgetc(fpin))))
{
if(ch == '.')fseek(fpin,-1L,SEEK_CUR);
token[i] = ch;
i++;
ch = fgetc(fpin);
}
fseek(fpin,-1L,SEEK_CUR);
//属于无符号常数
printf("%s\t11\tconstant\n",token);
fflush(stdout);
}
else switch(ch){
//运算符
case '+':{
ch = fgetc(fpin);
if(ch == '+')
{
printf("++\t12\toperator\n");
fflush(stdout);
}
else {
printf("+ \t13\toperator\n");
fflush(stdout);
fseek(fpin,-1L,SEEK_CUR);
}
}break;
case '-':{
ch = fgetc(fpin);
if(ch == '-')
{
printf("--\t14\toperator\n");
fflush(stdout);
}
else {
printf("- \t15\toperator\n");
fflush(stdout);
fseek(fpin,-1L,SEEK_CUR);
}
}break;
case '*':printf("%c\t16\toperateor\n",ch);break;
case '/':printf("%c\t17\toperateor\n",ch);break;
//分界符
case '(':printf("%c\t18\tboundary\n",ch);break;
case ')':printf("%c\t19\tboundary\n",ch);break;
case '[':printf("%c\t20\tboundary\n",ch);break;
case ']':printf("%c\t21\tboundary\n",ch);break;
case ';':printf("%c\t22\tboundary\n",ch);break;
case '{':printf("%c\t23\tboundary\n",ch);break;
case '}':printf("%c\t24\tboundary\n",ch);break;
case '#':printf("%c\t25\tboundary\n",ch);break;
//运算符
case '=':{
ch = fgetc(fpin);
if(ch == '=')printf("==\t26\toperator\n");
else {
printf("= \t27\toperator\n");
fseek(fpin,-1L,SEEK_CUR);
}
}break;
case ':':{
ch = fgetc(fpin);
if(ch == '=')printf(":=\t28\toperator\n");
else {
printf(": \t29\toperator\n");;
fseek(fpin,-1L,SEEK_CUR);
}
}break;
case '>':{
ch = fgetc(fpin);
if(ch == '=')printf(">=\t30\toperator\n");
else {
printf("> \t31\toperator\n");;
fseek(fpin,-1L,SEEK_CUR);
}
}break;
case '<':{
ch = fgetc(fpin);
if(ch == '=')printf("<=\t32\toperator\n");
else {
printf("< \t33\toperator\n");
fseek(fpin,-1L,SEEK_CUR);
}
}break;
//无识别
default: printf("%c\t34\tno_recognition\n",ch);
}
}
}
int main(){
char input[30];
FILE *fpin;
printf("Please input the path of file that you want to compile:(C:\...)\n ");
fflush(stdout);
scanf("%s",input);
if((fpin = fopen(input,"r")) != NULL)
{
printf("File open success!\n");
}
else
{
printf("Path Error!\n\n");
fflush(stdout);
}
printf("****************Lexical analysis start*********************\n");
analyze(fpin);
printf("****************Lexical analysis finish********************\n");
fclose(fpin);
printf("File close success!");
return 0;
}
调试结果:
参考资料:
[1]:https://www.cnblogs.com/ya-qiang/p/8987926.html