上一篇我们实现了一个简单的加法计算器,并且了解了基本的词法分析、词法分析器的概念。本篇我们将要对之前实现的加法计算器进行扩展,我们为它添加以下几个功能
- 计算减法
- 能自动识别并跳过空白字符
- 不再局限于单个整数,而是能计算多位整数
提供一些工具函数
首先为了支持减法,我们需要重新定义一下TokenType这个类型,也就是需要给 -
定义一个标志。现在我们的TokenType
的定义如下
typedef enum e_TokenType
{
CINT = 0,
PLUS,
MINUS,
END_OF_FILE
}ETokenType;
由于需要支持多个整数,所以我们也不知道最终会有多少个字符,因此我们提供一个END_OF_FILE
表示我们访问到了最后一个字符,此时应该退出词法分析的过程。
另外因为整数个数不再确定,我们也就不能按照之前的提供一个固定大小的数组。虽然可以提供一个足够大的空间来作为存储数字的缓冲,但是数字少了会浪费空间。而且考虑到之后要支持自定义变量和函数,采用固定长度缓冲的方式就很难找到合适的大小,太大显得浪费空间,太小有时候无法容纳得下用户定义的变量和函数名。因此这里我们采用动态长度的字符缓冲来保存。我们提供一个DyncString
的结构来保存这些内容
#define DEFAULT_BUFFER_SIZE 16
// 动态字符串结构,用于保存任意长度的字符串
typedef struct DyncString
{
int nLength; // 字符长度
int capacity; //实际分配的空间大小
char* pszBuf; //保存字符串的缓冲
}DyncString, *LPDyncString;
// 动态字符串初始化
// str: 被初始化的字符串
// size: 初始化字符串缓冲的大小,如果给0则按照默认大小分配空间
void dyncstring_init(LPDyncString str, int size);
// 动态字符串空间释放
void dyncstring_free(LPDyncString str);
//重分配动态字符串大小
void dyncstring_resize(LPDyncString str, int newSize);
//往动态字符串中添加字符
void dyncstring_catch(LPDyncString str, char c);
// 重置动态数组
void dyncstring_reset(LPDyncString str);
它们的实现如下
代码语言:javascript复制/*----------------------------动态数组的操作函数-------------------------------*/
void dyncstring_init(LPDyncString str, int size)
{
if (NULL == str)
return;
if (size == 0)
str->capacity = DEFAULT_BUFFER_SIZE;
else
str->capacity = size;
str->nLength = 0;
str->pszBuf = (char*)malloc(sizeof(char) * str->capacity);
if (NULL == str->pszBuf)
{
error("分配内存失败n");
}
memset(str->pszBuf, 0x00, sizeof(char) * str->capacity);
}
void dyncstring_free(LPDyncString str)
{
if (NULL == str)
return;
str->capacity = 0;
str->nLength = 0;
if (str->pszBuf == NULL)
return;
free(str->pszBuf);
}
void dyncstring_resize(LPDyncString str, int newSize)
{
int size = str->capacity;
for (; size < newSize; size = size * 2);
char* pszStr = (char*)realloc(str->pszBuf, size);
str->capacity = size;
str->pszBuf = pszStr;
}
void dyncstring_catch(LPDyncString str, char c)
{
if (str->capacity == str->nLength 1)
{
dyncstring_resize(str, str->capacity 1);
}
str->pszBuf[str->nLength] = c;
str->nLength ;
}
void dyncstring_reset(LPDyncString str)
{
dyncstring_free(str);
dyncstring_init(str, DEFAULT_BUFFER_SIZE);
}
/*----------------------------End 动态数组的操作函数-------------------------------*/
另外提供一些额外的工具函数,他们的定义如下
代码语言:javascript复制void error(char* lpszFmt, ...)
{
char szBuf[1024] = "";
va_list arg;
va_start(arg, lpszFmt);
vsnprintf(szBuf, 1024, lpszFmt, arg);
va_end(arg);
printf(szBuf);
exit(-1);
}
bool is_digit(char c)
{
return (c >= '0' && c <= '9');
}
bool is_space(char c)
{
return (c == ' ' || c == 't' || c == 'r' || c == 'n');
}
主要算法
我们还是延续之前的算法,一个字符一个字符的解析,只是现在需要额外的将多个整数添加到一块作为一个整数处理。而且需要添加跳过空格的处理。
首先我们对上次的代码进行一定程度的重构。我们添加一个函数专门用来获取下一个字符
代码语言:javascript复制char get_next_char()
{
// 如果到达字符串尾部,索引不再增加
if (g_pPosition == '