C语言之为表达式计算器实现定义变量和使用变量功能
- 上一篇实现了基本表达式的加减乘除和括号运算功能
- 这一次的目标是实现定义变量和使用变量功能
- 增加分号做为分隔符和结束符!!!
- 变量名为大小字母组合,长度不能超过7个字母!!!
- 定义变量格式: a = 9;
- 使用变量格式: a + 100;
重新定义数据类型
- 将原来的enum定义去掉!!!
/* define express datatype */
typedef enum _ExpressDataType ExprDT;
enum _ExpressDataType {
DT_OPERATOR,
DT_INTEGER,
};
- 直按改为宏定义!!!
/* define express node data type */
#define DT_OPERATOR 0X01
#define DT_INTEGER 0X02
#define DT_VARIABLE 0X03
将表达式结构体定义修改一下
- 增加成员字符数组name,做为保存变量名的空间!!!
- 将dt改为字符型,只占1个字节,name占7个字节,做到8字节对齐!!!
/* define express struct */
typedef struct _Express Expr;
struct _Express {
char name[7]; //variable name
char dt; //datatype
union {
char oval; //operator
long ival; //integer
} V;
};
修改输出信息函数
- 加入输出变量名信息,代码如下:
/* output express value */
void
out_value (Expr expr)
{
switch (expr.dt)
{
case DT_OPERATOR: printf (" %c", expr.V.oval); break;
case DT_INTEGER : printf (" %ld", expr.V.ival); break;
case DT_VARIABLE: printf (" %s", expr.name); break;
}
}
/* output express info */
void
out_info (Expr expr)
{
switch (expr.dt)
{
case DT_OPERATOR: printf (" op : %c\n", expr.V.oval); break;
case DT_INTEGER : printf ("int : %ld\n", expr.V.ival); break;
case DT_VARIABLE: printf ("var : %s\n", expr.name); break;
}
}
提前准备
- 将转换函数mid_torpn和计算函数compute_rpn_express的定义提前,以便parse_string函数来调用!!!
/* declare here !!! */
/* middle expr to rpn expr */
int
mid_torpn (Expr *expr, Expr *rpn, int elen);
/* compute rpn express */
Expr
compute_rpn_express (Expr *expr, int rlen);
- 定义get_value函数,根据变量名获取变量值
- 第一个参数vtab为变量表的指针
- 第二个参数vlen为变量表的长度
- 第三个参数name为变量名
/* get value by variable name */
Expr
get_value (Expr *vtab, int vlen, char *name)
{
Expr rs = {0, 0, 0};
for (int k = 0; k < vlen; k++)
{
if (0 == strcmp (name, vtab[k].name))
{
rs = vtab[k]; break;
}
}
return rs;
}
重新定义parse_string函数
- parse_string函数定义如下:
- 第一个参数estr为表达式字符串指针
- 第二个参数expr为表达式结构数组
- 第三个参数elen为表达式结构数组的长度
- 第四个参数vtab为变量表结构数组
- 第五个参数vlen为保存变量数量的整型指针
/* parse express string to Expr struct array */
int
parse_string (char *estr, //express string
Expr *expr, //express struct array
int elen, //express struct array length
Expr *vtab, //variable struct array
int *vlen) //how many variable struct in array
parse_string函数内部变量定义如下:
idx 表达式字符串索引,每读一个字符自增一
ep 表达式结构数组索引,每保存一个Expr自增一
nbuf 保存数字,长度为32
ndx nbuf的索引,每保存一个数字自增一
vbuf 保存变量名,长度为8
vdx vbuf的索引,每保存一个字母自增一,长度超过7则显示出错信息并退出运行!!!
aflag 赋值标志,发现等号后设置
vflag 变量标志,发现字母后设置
处理字母和等号
- 大小写字母 A-Za-z 长度小于等于7,超出则出错退出!!!
- 遇到等号’=',将字符串保存到变量表中去,同时清空vbuf,vdx置0,aflag置0!
代码如下:
case 'A'...'Z':
case 'a'...'z': //letter
{
vbuf[vdx] = c; vdx++; vflag = 1;
if (vdx == 8)
{
printf ("Error: Variable name length bigger than 7!\n");
exit (0);
}
}
break;
case '=': //assignment
vtab[*vlen].dt = DT_VARIABLE;
for (int i = 0; i < 7; i++)
vtab[*vlen].name[i] = vbuf[i];
memset (vbuf, 0, 7); vdx = 0; aflag = 1; vflag = 0;
break;
处理数字
- 保存数字至表达式结构数组
代码如下:
case '0'...'9': //number
{
char n = estr[idx+1];
nbuf[ndx] = c; ndx++;
if (n == ' ' || n == ')' || n == ';' || n == '\0'|| //number end
n == '+' || n == '-' || n == '*' || n == '/') //operator
{
long lt = strtol (nbuf, NULL, 10);
memset (nbuf, 0, 32); nbuf[0] = '+'; ndx = 1;
expr[ep].dt = DT_INTEGER;
expr[ep].V.ival = lt; ep++;
}
}
break;
处理分隔符(分号)
- 当vflag置1时,根据变量名取变量值,保存到表达式结构数组!
- 当aflag置1时,运算表达式,将结果赋值给变量!
代码如下:
case ';': //semicolon, Separator
{
if (vflag == 1)
{
Expr vs = get_value (vtab, *vlen, vbuf);
//printf ("GOT Variable [%s] : [%ld]\n", vbuf, vs.V.ival);
expr[ep].V.ival = vs.V.ival;
expr[ep].dt = DT_INTEGER; ep++;
memset (vbuf, 0, 7); vdx = 0; vflag = 0;
}
if (aflag == 1) //compute express
{
int len = ep, rlen;
Expr rs;
Expr rpn[128];
//if (ep == 0) return 0; //do nothing
rlen = mid_torpn (expr, rpn, ep); //len);
printf ("--------------------\n");
rs = compute_rpn_express (rpn, rlen);
printf ("--------------------\n");
printf ("Result : [%ld]\n", rs.V.ival);
vtab[*vlen].V.ival = rs.V.ival;
printf ("Variable name : [%s], value : [%ld]\n",
vtab[*vlen].name, vtab[*vlen].V.ival);
*vlen = *vlen + 1;
return 0;
}
}
break;
处理运算符加减乘除括号
- 处理数字的符号,也就是负号问题!
- 如果vflag置1,查找变量表,将变量的值存入表达式!
- 保存运算符至表达式结构数组!
代码如下:
case '+': case '-': case '*': case '/': case '(': case ')': //operator
{
if (c == '-') //negative number
{
char n = estr[idx+1];
if (n >= '0' && n <= '9')
{
printf ("Info : negative number!\n");
nbuf[0] = c; break;
}
}
if (vflag == 1)
{
Expr vs = get_value (vtab, *vlen, vbuf);
//printf ("GOT Variable [%s] : [%ld]\n", vbuf, vs.V.ival);
expr[ep].V.ival = vs.V.ival;
expr[ep].dt = DT_INTEGER; ep++;
memset (vbuf, 0, 7); vdx = 0; vflag = 0;
}
//printf ("save a operator : %c\n", c); //save op
expr[ep].dt = DT_OPERATOR;
expr[ep].V.oval = c; ep++;
}
break;
交互运算表达式循环
- 定义表达式字符串es,用来保存从标准输入的字符,定义变量idx为索引
- 定义表达式结构数组expr,用来保存表达式
- 定义RPN表达式结构数组rpn,用来保存RPN表达式
- 定义变量表达式结构数组vart,用来保存变量
- 定义相关的长度整型变量:
- len,从字符串解析得到的表达式的长度
- rlen,转换为RPN表达式后,RPN表达式的长度
- vlen,变量表的长度
- 定义返回结果变量rs
- 如果是变量定义,如:a=9;,则parse_string函数返回值为0,说明无表达式,只有变量定义!!!
代码如下:
/* evalute express */
void
eval_express (void)
{
char es[1024] = {0}; //save string from stdin
Expr expr[128]; //save express parse from string
Expr rpn[128]; //save rpn express from express
Expr vart[128]; //save variable express parse from string
Expr rs; //the result
int idx = 0; //index of es
int len = 0; //length of express parse from string
int rlen = 0; //rpn express length from express
int vlen = 0; //variable express length parse from string
char c;
printf ("EXPR:> ");
c = fgetc (stdin);
while (c != EOF)
{
if (c == '\n')
{
len = parse_string (es, expr, 128, vart, &vlen);
if (len == 0)
{
//get a variable declare express
memset (es, 0, 1024); len = 0; rlen = 0; idx = 0;
printf ("EXPR:> ");
c = fgetc (stdin);
}
else
{
rlen = mid_torpn (expr, rpn, len);
printf ("------------------------------\n");
for (int i = 0; i < rlen; i++)
out_value (rpn[i]);
printf ("\n------------------------------\n");
rs = compute_rpn_express (rpn, rlen);
printf ("------------------------------\n");
printf (" Result : %ld\n", rs.V.ival);
memset (es, 0, 1024);
len = 0; rlen = 0; idx = 0;
printf ("EXPR:> ");
c = fgetc (stdin);
}
}
else
{
es[idx] = c;
idx++; c = fgetc (stdin);
}
}
}
测试表达式
常规表达式
- 100+200-300; 100*200/400;
- 10+2030-50; (10+20)(50-30);
- (10+(30-20))50; 10+2030/40;
编译运行,达到预期,效果如下:
songvm@ubuntu:~/works/tmpc/boo$ gcc -g pt.c -o pt
songvm@ubuntu:~/works/tmpc/boo$ ./pt
EXPR:> 100+200-300;
------------------------------
100 200 + 300 -
------------------------------
push 100
push 200
operate : 100 + 200 = 300
push 300
operate : 300 - 300 = 0
------------------------------
Result : 0
EXPR:> 100*200/400;
------------------------------
100 200 * 400 /
------------------------------
push 100
push 200
operate : 100 * 200 = 20000
push 400
operate : 20000 / 400 = 50
------------------------------
Result : 50
EXPR:> 10+20*30-50;
------------------------------
10 20 30 * + 50 -
------------------------------
push 10
push 20
push 30
operate : 20 * 30 = 600
operate : 10 + 600 = 610
push 50
operate : 610 - 50 = 560
------------------------------
Result : 560
EXPR:> (10+20)*(50-30);
------------------------------
10 20 + 50 30 - *
------------------------------
push 10
push 20
operate : 10 + 20 = 30
push 50
push 30
operate : 50 - 30 = 20
operate : 30 * 20 = 600
------------------------------
Result : 600
EXPR:> (10+(30-20))*50;
------------------------------
10 30 20 - + 50 *
------------------------------
push 10
push 30
push 20
operate : 30 - 20 = 10
operate : 10 + 10 = 20
push 50
operate : 20 * 50 = 1000
------------------------------
Result : 1000
EXPR:> 10+20*30/40;
------------------------------
10 20 30 * 40 / +
------------------------------
push 10
push 20
push 30
operate : 20 * 30 = 600
push 40
operate : 600 / 40 = 15
operate : 10 + 15 = 25
------------------------------
Result : 25
EXPR:>
变量表达式
- 变量简单赋值:a = 100;
- 变量表达式赋值:b = 9 + 10;
- 变量赋值变量:c = a + b;
- 变量赋值变量表达式:d = c - a + b;
编译运行,基本达到预期,效果如下:
songvm@ubuntu:~/works/tmpc/boo$ gcc -g pt.c -o pt
songvm@ubuntu:~/works/tmpc/boo$ ./pt
EXPR:> a = 100;
--------------------
push 100
--------------------
Result : [100]
Variable name : [a], value : [100]
EXPR:> b = 9 + 10;
--------------------
push 9
push 10
operate : 9 + 10 = 19
--------------------
Result : [19]
Variable name : [b], value : [19]
EXPR:> c = a + b;
--------------------
push 100
push 19
operate : 100 + 19 = 119
--------------------
Result : [119]
Variable name : [c], value : [119]
EXPR:> d = c - a + b;
--------------------
push 119
push 100
operate : 119 - 100 = 19
push 19
operate : 19 + 19 = 38
--------------------
Result : [38]
Variable name : [d], value : [38]
EXPR:> a * a - c + d;
------------------------------
100 100 * 119 - 38 +
------------------------------
push 100
push 100
operate : 100 * 100 = 10000
push 119
operate : 10000 - 119 = 9881
push 38
operate : 9881 + 38 = 9919
------------------------------
Result : 9919
EXPR:>
完整源码如下:
/* filename: pt.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* compile : gcc pt.c -o pt */
/* run : ./pt */
/* define express node data type */
#define DT_OPERATOR 0X01
#define DT_INTEGER 0X02
#define DT_VARIABLE 0X03
/* define express struct */
typedef struct _Express Expr;
struct _Express {
char name[7]; //variable name
char dt; //express datatype
union {
char oval; //operator
long ival; //integer
} V;
};
/* output express value */
void
out_value (Expr expr)
{
switch (expr.dt)
{
case DT_OPERATOR: printf (" %c", expr.V.oval); break;
case DT_INTEGER : printf (" %ld", expr.V.ival); break;
case DT_VARIABLE: printf (" %s", expr.name); break;
}
}
/* output express info */
void
out_info (Expr expr)
{
switch (expr.dt)
{
case DT_OPERATOR: printf (" op : %c\n", expr.V.oval); break;
case DT_INTEGER : printf ("int : %ld\n", expr.V.ival); break;
case DT_VARIABLE: printf ("var : %s\n", expr.name); break;
}
}
/* declare here !!! */
/* middle expr to rpn expr */
int
mid_torpn (Expr *expr, Expr *rpn, int elen);
/* compute rpn express */
Expr
compute_rpn_express (Expr *expr, int rlen);
/* get value by variable name */
Expr
get_value (Expr *vtab, int vlen, char *name)
{
Expr rs = {0, 0, 0};
for (int k = 0; k < vlen; k++)
{
if (0 == strcmp (name, vtab[k].name))
{
rs = vtab[k]; break;
}
}
return rs;
}
/* parse express string to Expr struct array */
int
parse_string (char *estr, //express string
Expr *expr, //express struct array
int elen, //express struct array length
Expr *vtab, //variable struct array
int *vlen) //how many variable struct in array
{
char nbuf[32] = {0}; //save number
int ndx = 1; //number index
int idx = 0; //express string index
int ep = 0; //expr pointer
char vbuf[8] = {0}; //save variable name
int vdx = 0; //variable name index
int aflag = 0, vflag = 0; //assignment flag, variable flag
char c;
nbuf[0] = '+';
c = estr[idx];
while (c != 0)
{
switch (c)
{
case 'A'...'Z':
case 'a'...'z': //letter
{
vbuf[vdx] = c; vdx++; vflag = 1;
if (vdx == 8)
{
printf ("Error: Variable name length bigger than 7!\n");
exit (0);
}
}
break;
case '=': //assignment
vtab[*vlen].dt = DT_VARIABLE;
for (int i = 0; i < 7; i++)
vtab[*vlen].name[i] = vbuf[i];
memset (vbuf, 0, 7); vdx = 0; aflag = 1; vflag = 0;
break;
case ';': //semicolon, Separator
{
if (vflag == 1)
{
Expr vs = get_value (vtab, *vlen, vbuf);
//printf ("GOT Variable [%s] : [%ld]\n", vbuf, vs.V.ival);
expr[ep].V.ival = vs.V.ival;
expr[ep].dt = DT_INTEGER; ep++;
memset (vbuf, 0, 7); vdx = 0; vflag = 0;
}
if (aflag == 1) //compute express
{
int len = ep, rlen;
Expr rs;
Expr rpn[128];
//if (ep == 0) return 0; //do nothing
rlen = mid_torpn (expr, rpn, ep); //len);
printf ("--------------------\n");
rs = compute_rpn_express (rpn, rlen);
printf ("--------------------\n");
printf ("Result : [%ld]\n", rs.V.ival);
vtab[*vlen].V.ival = rs.V.ival;
printf ("Variable name : [%s], value : [%ld]\n",
vtab[*vlen].name, vtab[*vlen].V.ival);
*vlen = *vlen + 1;
return 0;
}
}
break;
case '0'...'9': //number
{
char n = estr[idx+1];
nbuf[ndx] = c; ndx++;
if (n == ' ' || n == ')' || n == ';' || n == '\0'|| //number end
n == '+' || n == '-' || n == '*' || n == '/') //operator
{
long lt = strtol (nbuf, NULL, 10);
memset (nbuf, 0, 32); nbuf[0] = '+'; ndx = 1;
expr[ep].dt = DT_INTEGER;
expr[ep].V.ival = lt; ep++;
}
}
break;
case '+': case '-': case '*': case '/': case '(': case ')': //operator
{
if (c == '-') //negative number
{
char n = estr[idx+1];
if (n >= '0' && n <= '9')
{
printf ("Info : negative number!\n");
nbuf[0] = c; break;
}
}
if (vflag == 1)
{
Expr vs = get_value (vtab, *vlen, vbuf);
//printf ("GOT Variable [%s] : [%ld]\n", vbuf, vs.V.ival);
expr[ep].V.ival = vs.V.ival;
expr[ep].dt = DT_INTEGER; ep++;
memset (vbuf, 0, 7); vdx = 0; vflag = 0;
}
//printf ("save a operator : %c\n", c); //save op
expr[ep].dt = DT_OPERATOR;
expr[ep].V.oval = c; ep++;
}
break;
case ' ': //space do nothing
break;
default:
printf ("Error: Syntax error!\n");
exit (0);
}
idx++; c = estr[idx]; //next char
}
return ep;
}
/* get priority level */
static char
get_plv (char x)
{
if (x == '+') return 1;
if (x == '-') return 1;
if (x == '*') return 2;
if (x == '/') return 2;
return 0;
}
/* middle expr to rpn expr */
int
mid_torpn (Expr *expr, Expr *rpn, int elen)
{
Expr st[8];
int idx = 0, ip = 0;
for (int j = 0; j < 8; j++)
{ st[j].dt = 0; st[j].V.oval = 0; }
for (int i = 0; i < elen; i++)
{
Expr et = expr[i];
switch (et.dt)
{
case DT_INTEGER: //number
rpn[idx].dt = DT_INTEGER;
rpn[idx].V.ival = et.V.ival; idx++;
break;
case DT_OPERATOR: //operator
{
switch (et.V.oval)
{
case '+': case '-': case '*': case '/': //operator
{
char lv, lx;
if (ip == 0) //stack empty
{
st[ip].dt = DT_OPERATOR;
st[ip].V.oval = et.V.oval; ip++; //push
break;
}
if (st[ip-1].dt == DT_OPERATOR && st[ip-1].V.oval == '(')
{
st[ip].dt = DT_OPERATOR;
st[ip].V.oval = et.V.oval; ip++; //push
break;
}
lv = get_plv (et.V.oval);
lx = get_plv (st[ip-1].V.oval);
if (lv > lx)
{
st[ip].dt = DT_OPERATOR;
st[ip].V.oval = et.V.oval; ip++; //push
break;
}
else
{
rpn[idx].dt = DT_OPERATOR;
rpn[idx].V.oval = st[ip-1].V.oval; idx++; //out
st[ip-1].dt = 0; st[ip-1].V.oval = 0; ip--; //pop
while (ip > 0)
{
char lt = get_plv (st[ip-1].V.oval);
if (lv > lt)
{
st[ip].dt = DT_OPERATOR;
st[ip].V.oval = et.V.oval; ip++; //push
break;
}
else
{
rpn[idx].dt = DT_OPERATOR;
rpn[idx].V.oval = st[ip-1].V.oval; idx++; //out
st[ip-1].dt = 0; st[ip-1].V.oval = 0; ip--; //pop
}
}
if (ip == 0)
{
st[ip].dt = DT_OPERATOR;
st[ip].V.oval = et.V.oval; ip++; //push
}
}
}
break;
case '(':
st[ip].dt = DT_OPERATOR;
st[ip].V.oval = et.V.oval; ip++; //push
break;
case ')':
{
while (ip > 0)
{
char ot = st[ip-1].V.oval;
if (ot == '(')
{
st[ip-1].dt = 0; st[ip-1].V.oval = 0; ip--; //pop
break;
}
else
{
rpn[idx].dt = DT_OPERATOR;
rpn[idx].V.oval = st[ip-1].V.oval; idx++; //out
st[ip-1].dt = 0; st[ip-1].V.oval = 0; ip--; //pop
}
}
}
break;
}
}
break;
}
}
//pop until stack empty
while (ip > 0)
{
rpn[idx].dt = DT_OPERATOR;
rpn[idx].V.oval = st[ip-1].V.oval; idx++; //out
st[ip-1].dt = 0; st[ip-1].V.oval = 0; ip--; //pop
}
return idx;
}
/* compute rpn express */
Expr
compute_rpn_express (Expr *expr, int rlen)
{
Expr st[16], rs;
int ip = 0;
for (int j = 0; j < 16; j++)
{ st[j].dt = 0; st[j].V.oval = 0;}
for (int i = 0; i < rlen; i++)
{
Expr et = expr[i];
if (et.dt == DT_INTEGER)
{
st[ip].dt = DT_INTEGER; st[ip].V.ival = et.V.ival; ip++;
printf ("push %ld\n", et.V.ival);
}
else if (et.dt == DT_OPERATOR)
{
switch (et.V.oval)
{
case '+':
{
printf ("operate : %ld + %ld = ",
st[ip-2].V.ival, st[ip-1].V.ival);
st[ip-2].V.ival = st[ip-2].V.ival + st[ip-1].V.ival;
printf ("%ld\n", st[ip-2].V.ival);
st[ip-1].V.oval = 0; ip--;
}
break;
case '-':
{
printf ("operate : %ld - %ld = ",
st[ip-2].V.ival, st[ip-1].V.ival);
st[ip-2].V.ival = st[ip-2].V.ival - st[ip-1].V.ival;
printf ("%ld\n", st[ip-2].V.ival);
st[ip-1].V.ival = 0; ip--;
}
break;
case '*':
{
printf ("operate : %ld * %ld = ",
st[ip-2].V.ival, st[ip-1].V.ival);
st[ip-2].V.ival = st[ip-2].V.ival * st[ip-1].V.ival;
printf ("%ld\n", st[ip-2].V.ival);
st[ip-1].V.ival = 0; ip--;
}
break;
case '/':
{
printf ("operate : %ld / %ld = ",
st[ip-2].V.ival, st[ip-1].V.ival);
st[ip-2].V.ival = st[ip-2].V.ival / st[ip-1].V.ival;
printf ("%ld\n", st[ip-2].V.ival);
st[ip-1].V.ival = 0; ip--;
}
break;
}
}
}
rs.dt = DT_INTEGER;
rs.V.ival = st[0].V.ival;
return rs;
}
/* evalute express */
void
eval_express (void)
{
char es[1024] = {0}; //save string from stdin
Expr expr[128]; //save express parse from string
Expr rpn[128]; //save rpn express from express
Expr vart[128]; //save variable express parse from string
Expr rs; //the result
int idx = 0; //index of es
int len = 0; //length of express parse from string
int rlen = 0; //rpn express length from express
int vlen = 0; //variable express length parse from string
char c;
printf ("EXPR:> ");
c = fgetc (stdin);
while (c != EOF)
{
if (c == '\n')
{
len = parse_string (es, expr, 128, vart, &vlen);
if (len == 0)
{
//get a variable declare express
memset (es, 0, 1024); len = 0; rlen = 0; idx = 0;
printf ("EXPR:> ");
c = fgetc (stdin);
}
else
{
rlen = mid_torpn (expr, rpn, len);
printf ("------------------------------\n");
for (int i = 0; i < rlen; i++)
out_value (rpn[i]);
printf ("\n------------------------------\n");
rs = compute_rpn_express (rpn, rlen);
printf ("------------------------------\n");
printf (" Result : %ld\n", rs.V.ival);
memset (es, 0, 1024);
len = 0; rlen = 0; idx = 0;
printf ("EXPR:> ");
c = fgetc (stdin);
}
}
else
{
es[idx] = c;
idx++; c = fgetc (stdin);
}
}
}
/**/
int
main (int argc, char *argv[])
{
eval_express ();
//printf ("Sizeof Expr : %lu\n", sizeof(Expr));
return 0;
}
//-----(+-*/)-----//
- 测试虽然通过,感觉还不够充分,盼网友测试回复!
- 下一步研究给表达式加个调用函数功能!!!