在上一篇我们实现了一个可以计算两个多位整数加减法的计算器。本章我们继续来给这个计算器添加功能,这次要给它添加可以连续计算多个整数相加减的功能。例如我们可以计算 1 2 3
这样的表达式。
语法图
在正式写代码之前让我们先来学习一下一些基本的理论知识。这次要介绍的理论是语法图。
什么是语法图呢?语法图是编程语言语法语法规则的图形表示。它体现了词法分析的运行规则。语法图直观的展示了在编程语言中哪些语句是符合语法的,哪些是不符合语法规范的。
语法图的阅读非常容易,它类似于程序的流程图,只要顺着箭头指向的路径来读即可。与程序流程图类似,语法图中有些路径表示选择,有些表示循环。我们试着来读一下下面的语法图
这张语法图表示的含义是,一个术语(term) 可选的跟上一个加号或者减号,而后面又需要跟上另一个术语。接着又可以有选择的跟上另一个加号或者减号。但是加号或者减号后面必须跟上另一个术语。
这里又提到另一个单词,term
它的中文意思是术语。似乎很难用其他文字来解释何为术语。你只需要知道在这里它代表的是一个整数,它并不影响我们阅读这个语法图
代码展示
在上一篇中我们提到,将Token流识别为对应结构的过程被称之为词法分析,我们代码中的词法分析的实现主要在函数 expr
中。在这个函数中我们主要实现了词法分析以及最后的解释执行。我们按照语法图修改一下词法分析的代码
我们先给出下面的伪代码
代码语言:javascript复制获取第一个整数作为计算结果保存
while(解析到最后一个字符)
{
获取操作符( /-)
switch(操作符)
{
case :
获取下一个整数,如果不是整数则退出并报错
与结果相加
break;
case -:
获取下一个整数,如果不是整数则退出并报错
与结果相减
break;
}
}
最终打印计算结果或者打印语法错误
基于这个思路我们给出具体的实现代码
代码语言:javascript复制int expr()
{
bool bRet = false;
int result = get_term(&bRet);
int bEOF = false;
do
{
ETokenType oper = get_oper(&bRet);
switch (oper)
{
case PLUS:
{
int num = get_term(&bRet);
if(bRet)
result = num;
}
break;
case MINUS:
{
int num = get_term(&bRet);
if(bRet)
result -= num;
}
break;
case END_OF_FILE:
printf("%dn", result);
bEOF = true;
break;
default:
bRet = false;
break;
}
} while (bRet && !bEOF);
if (!bRet)
{
printf("Syntax Error!n");
}
}
这里为了便于理解,我将获取整数和操作符的模块又进行了一次封装,提供了两个函数分别是 get_term()
和 get_oper()
。它们的代码如下
int get_term(bool *pRet)
{
Token token = { 0 };
dyncstring_init(&token.value, DEFAULT_BUFFER_SIZE);
int value = 0;
if (get_next_token(&token) && token.type == CINT)
{
value = atoi(token.value.pszBuf);
if (pRet)
*pRet = true;
}
else
{
if (pRet)
*pRet = false;
}
dyncstring_free(&token.value);
return value;
}
代码语言:javascript复制ETokenType get_oper(bool* pRet)
{
Token token = { 0 };
dyncstring_init(&token.value, DEFAULT_BUFFER_SIZE);
int oper = 0;
if (get_next_token(&token) && (token.type == PLUS || token.type == MINUS))
{
oper = token.type;
if (pRet)
*pRet = true;
}
else if (token.type == END_OF_FILE)
{
oper = END_OF_FILE;
if (pRet)
*pRet = true;
}
else
{
oper = -1;
if (pRet)
*pRet = false;
}
dyncstring_free(&token.value);
return oper;
}
到此为止,就实现了多个整数的算术运算。整个实现过程的代码我都放到该位置。有兴趣的小伙伴可以自己对照着代码跟着我一起来实现属于自己的解释器。