本文通过学习王博俊、张宇的《DIY Compiler and Linker》 ,实现词法分析器,一方面作为自己的学习笔记,一方面也作与大家分享与交流
词法分析的任务
源程序由字符序列构成,词法分析器扫描源程序字符串,根据语言的词法规则分析并识别具有独立意义的最小语法单位:单词(包括关键字、运算符、标识符等),并以某种编码形式输出。
词法分析流程图
这次实现的词法分析器完全按照下图所编写:
从图中可以看到
1. 先打开源文件,如果无法打开则异常退出
2. 打开源文件后进行初始化,标致行号为第一行,再初始化动态数组,并把运算符、关键字、常量直接放入单词表(单词表是由动态数组和哈希表一起组成的复合结构)
3. 开始读取一个字符,并从第一个字符开始进行“取单词”的工作,如果遇到空白字符或者注释则忽略,如果是有效字符则判断这一单词并给出编号或者标识符索引码
4. 为了能显示出来我们的词法分析,对不同类别的单词进行着色输出到我们屏幕上
5. 循环往复第3、4步直到文件末尾,关闭文件,正常结束
实现词法分析器
1. 自定义动态字符串和动态数组
自定义动态字符串和动态数组是词法分析需要用到的数据结构
- 首先,常量字符串的长度没办法预知,可能是空串,也可能很长,则需要动态字符串来存储
- 其次,单词的个数也无法预知,可能只有一个单词,也可能有许多,对于单词的存储,也需要一个按需分配的动态数组
词法分析器是用C语言写的,需要自定义动态字符串和动态数组
1.1 动态字符串
dynstring.h(实际上放在头文件lexical_analysis.h):
/*动态字符串定义*/
typedef struct DynString
{
int count; //字符串长度
int capacity; //包含该字符串的缓冲区长度
char *data; //指向字符串的指针
}DynString;
void dynstring_init(DynString *pstr, int initsize);//初始化动态字符串存储容量,用于dynstring_reset()
void dynstring_free(DynString *pstr);//释放动态字符串使用的内存空间,用于dynstring_reset()
void dynstring_realloc(DynString *cstr, int new_size);//重新分配字符串容量
void dynstring_chcat(DynString *cstr, int ch);//字符串中添加字符
void dynstring_reset(DynString *cstr);//重置动态字符串
dynstring.c:
#include "lexical_analysis.h"
/***********************************************************
* 功能: 初始化动态字符串储存容量
* pstr: 动态字符串指针
* initsize: 字符串初始化分配空间
**********************************************************/
void dynstring_init(DynString *pstr, int initsize)
{
if (pstr != NULL)
{
pstr->data = (char *)malloc(sizeof(char) * initsize);
pstr->count = 0;
pstr->capacity = initsize;
}
}
/***********************************************************
* 功能: 释放动态字符串使用的内存空间
* pstr: 动态字符串指针
**********************************************************/
void dynstring_free(DynString *pstr)
{
if (pstr != NULL)
{
if (pstr->data) free(pstr->data);
pstr->count = 0;
pstr->capacity = 0;
}
}
/***********************************************************
* 功能: 重置动态字符串,先释放,重新初始化
* pstr: 动态字符串指针
**********************************************************/
void dynstring_reset(DynString *pstr)
{
dynstring_free(pstr);
dynstring_init(pstr, 10);//初始化字符串分配10个字节空间
}
/***********************************************************
* 功能: 重新分配字符串容量
* pstr: 动态字符串指针
* new_size: 字符串新长度
**********************************************************/
void dynstring_realloc(DynString *pstr, int new_size)
{
int capacity;
char *data;
capacity = pstr->capacity;
while (capacity < new_size)
{
capacity *= 2;//本来分配空间扩大一倍
}
data = (char *)realloc(pstr->data, capacity);
if (!data)//realloc 返回NULL 即分配失败
error("内存分配失败");
pstr->capacity = capacity;
pstr->data = data;
}
/***********************************************************
* 功能: 追加单个字符到动态字符串对象
* pstr: 动态字符串指针
* ch: 所要追加的字符
**********************************************************/
void dynstring_chcat(DynString *pstr, int ch)
{
int count;
count = pstr->count + 1;
if (count > pstr->capacity)
dynstring_realloc(pstr, count);
pstr->data[count - 1] = ch;
pstr->count = count;
}
1.2 动态数组
dynarray.h(实际上放在头文件lexical_analysis.h):
/*动态数组定义*/
typedef struct DynArray
{
int count;//动态数组元素个数
int capacity;//动态数组缓冲区长度
void **data;//指向数据指针的数组
}DynArray;
void dynarray_realloc(DynArray *parr, int new_size);//重新分配动态数组容量,用于dynarray_add()函数
void dynarray_add(DynArray *parr, void *data);//追加动态数组元素
void dynarray_init(DynArray *parr, int initsize);//初始化动态数组存储容量
void dynarray_free(DynArray *parr);//释放动态数组使用的内存空间
int dynarray_search(DynArray *parr, int key);//动态数组元素查找
dynarray.c:
#include "lexical_analysis.h"
/***********************************************************
* 功能: 初始化动态数组储存容量
* parr: 动态数组指针
* initsize: 动态数组初始化分配空间
**********************************************************/
void dynarray_init(DynArray *parr, int initsize)
{
if (parr != NULL)
{
parr->data = (void **)malloc(sizeof(void*) * initsize);
parr->count = 0;
parr->capacity = initsize;
}
}
/***********************************************************
* 功能: 释放动态数组使用的内存空间
* parr: 动态数组指针
**********************************************************/
void dynarray_free(DynArray *parr)
{
void **p;
for (p = parr->data; parr->count; ++p, --parr->count)
{
if (*p)
free(*p);
}
free(parr->data);
parr->data = NULL;
}
/***********************************************************
* 功能: 重新分配动态数组容量
* parr: 动态数组指针
* new_size: 动态数组最新元素个数
**********************************************************/
void dynarray_realloc(DynArray *parr, int new_size)
{
int capacity;
void *data;
capacity = parr->capacity;
while (capacity < new_size)
capacity *= 2;
data = realloc(parr->data, capacity);
if (!data)
error("内存分配失败");
parr->capacity = capacity;
parr->data = (void **)data;
}
/***********************************************************
* 功能: 追加动态数组元素
* parr: 动态数组指针
* data: 所要追加的新元素
*********************************************<