C语言实现简单解释器(interpreter)

0. 写在前面

本项目通过C语言实现一个简单的类似于matlab的解释器(interpreter)。支持数学表达式的计算与变量存储、计算。非法输入与报错尚未支持。

整个实现过程是由小到大,由少到多的,基本流程如下:

  • 实现读写数据
  • 实现逆波兰加减乘除
  • 实现逆波兰括号
  • 实现逆波兰式完整转化
  • 实现分割操作符与操作数
  • 实现逆波兰式的计算
  • 实现对负数的识别与计算
  • 实现对任意表达式的计算(Milestone)
  • 实现变量赋值式的转化
  • 实现变量的识别
  • 实现变量的数值存储与使用
  • 实现有变量式子的计算
  • 实现变量值变化后式子的计算
  • 实现分号功能
  • 通过JOJ!

然而本文档并不会每一步给出指导(懒)只会给出一些关键问题的解答,如果还有疑惑,请首先使用你的大脑,再使用搜索引擎,再使用TA,最后使用你的倒霉室友。

注:本文档中的大部分代码是伪代码(seudo code),仅作为基本的示范,不可直接复制黏贴!请你理解后模仿写出属于自己的代码!

1. stack的操作与定义

1.1 栈的性质

栈是一种特殊的数组,数组中的元素先进后出,后进先出,可以类比为一个羽毛球桶,先进去的羽毛球最后面才能拿出来。为了体现“先进后出,后进先出”的重要特性,我们规定pushpop两种操作。push将元素塞入数组最上方,pop将数组最上方的元素弹走。

1.2 push(压栈)pop(弹栈):

int push(char *stack, int top, char element);       // push char
int push_d(double *stack, int top, double element); // push double
int pop(char *stack, int top);                      // pop char
int pop_d(double *stack, int top);                  // pop double

poppush均为栈的常用操作,因此通过自定义函数来实现。

自定义时注意以下三点:

  1. pop/push完后,top在哪?
  2. pop/push对象元素是谁?
  3. pop/push目标数组是谁?
  4. 栈的数据类型
  5. 将以上问题的答案作为你的函数参数/输出。

Sample Code:

int push(char *stack, int top, char input){stack[top++] = input;return top;
}int pop(char *stack, int top){if (top == 0){return 0;}out1 = stack[--top];return top;
}

自定义好stack操作函数后,开一个新的practice.c熟练你的stack操作!

修改自己的poppush函数,使其符合自己的思维习惯与逻辑。

注意:只有你知道你的stack应该怎么操作!

Sample Code:

#include <stdio.h>char out;
int push(char *stack, int top, char element);
int pop(char *stack, int top);int main()
{char stack1[120];char stack2[120];int top1 = 0;int top2 = 0;//top初始值为0,否则会越界top1 = push(stack1, top1, 'a');top1 = push(stack1, top1, 'b');top2 = push(stack2, top2, 'c');top1 = pop(stack1, top1);top2 = pop(stack2, top2);printf("%c\n", stack1[top1]);printf("%c\n", stack2[top1]);return 0;
}int push(char*stack, int top, char input){stack[top++] = input;//注意:这里的top++与++top天差地别!自己思考为什么!return top;
}int pop(char*stack, int top){if (top == 0)return -1;out = stack[--top];//注意:这里的--top与top--天差地别!自己思考为什么!return top;
}

2. stack在逆波兰表达式中的实战

2.1 逆波兰表达式RPE(Reverse Polish Expression)

如果你仍然对RPE有不解之处,或者你无法熟练地人工写出一个正常表达式的逆波兰表达式,请你先理解逆波兰表达式的操作后再开始coding!

逆波兰表达式由某个波兰人发明,由于发明者名字太难读,所以取名“Polish”。逆波兰表达式的本质是将一个中缀表达式(infix),i.e.操作符(operator)前后连接两个操作数(operand)(e.g. (0 /a+ 0 / 1+ 0/3 + 0/a + b * 2.5)* b^(-2))变为后缀表达式(postfix),i.e.,操作符在操作数的后面 **(e.g. ,0,a,/,0,1,/,+,0,3,/,+,0,a,/,+,b,2.5,+,b,0,2,-,^,)**由此便可以通过栈的数据结构便于计算机的运算与操作。

2.2 逆波兰表达式的转化

2.2.0. 预备工作

要实现逆波兰表达式的转换,你需要三个数组:

  1. 待转换式子的数组 infix
  2. 放符号的load栈 load_stack
  3. print最终转换后结果的输出栈 RPE

2.2.1. 开始转换

从左至右扫描中缀表达式。

遇到操作数时,将其压入转换后结果的输出栈RPE

遇到运算符时,比较其与 load_stack栈顶运算符的优先级:

  1. 如果load_stack为空,或栈顶运算符为左括号(,则直接将此运算符入栈。

  2. 否则,若优先级小于或等于栈顶运算符优先级,将load_stack栈顶的运算符弹出并压入到RPE中。将当前运算符压入栈中load_stack

  3. 再次转到 1 与load_stack中新的栈顶运算符相比较。

遇到括号时:

  1. 如果是左括号(,则直接压入load_stack

  2. 如果是右括号),则依次弹出load_stack栈顶的运算符,并压入RPE,直到遇到左括号为止,此时将这一对括号丢弃;
    重复步骤2至5,直到表达式的最右边。

  3. load_stack中剩余的运算符依次弹出并压入RPE。

整个流程都非常抽象,表格法并不直观。所以强烈建议看视频或者动图进行直观理解。

可参考资源:

https://blog.csdn.net/yuan_xw/article/details/104436091

https://youtu.be/7ha78yWRDlE

完美的逆波兰表达式应该只有数字与加减乘除等,没有括号

2.2.3 实现

在基本理解转换过程后,拿几个式子进行人工计算!可用这个网站https://www.mathblog.dk/tools/infix-postfix-converter/ 进行检验。

感觉自己基本掌握了之后,就开始coding吧!

在转换之前,请你首先解决这个不难但是总是实现不了的问题:

如何将读写txt文本的数据?

  1. 推荐以下函数:

fopen(), feof(),fgets()

他们的具体功能与使用请自己搜索(真的不难!)

  1. 文本数据可以存放在一个二维数组中:
char data[LINE][LENGTH]
  1. 然后你就应该知道怎么办啦!

编程时思考以下问题:

  1. 你眼前有三个数组,分别给他们取上你喜欢并且易于识别的名字
  2. 这三个数组哪两个是stack?每个stacktop又如何设定?
  3. 符号的优先级该如何比较?hint:写一个比较优先级的函数。
  4. 括号的操作应该怎么办?弹出load_stack元素时会不会弹空?
  5. 别忘记最后清空!

2.3 逆波兰表达式的计算

2.3.1 在确保正确的逆波兰表达式后,计算则相对简单不容易出错。手动计算方法如下:

  1. 找到一个操作符

  2. 找该操作符前两个操作数

  3. 将这两个操作数按照该操作符进行计算,得出结果

  4. 将这两个操作数与操作符删掉,替换成3中计算的结果

如何让计算机实现以上操作过程?

但是聪明的你应该想到,要想计算必须要让计算机认识哪个是操作符,哪个是操作数

因此我们不得不进行句法分析

2.3.2 句法分析(parsing)

句法分析,顾名思义就是让计算机能够识别字符串中的合法的词句。

Eg1:一个string:iloveprettygirls 你的眼里:i love pretty girls计算机眼里:i l o v e p r e t t y g i r l s Eg2:一个string:25+36/0.23-(34-3)你的眼里:25+36/0.23-(34-3)计算机眼里:2 5 + 3 6 / 0 . 2 3 - ( 3 4 - 3 )

所以,我们的目的就是让计算机眼里看出来的和我们人类眼里看出来的是一样的,这就是句法分析。

那如何句法分析呢?在这里给出一个hint:在push符号的时候,顺便push一个分隔符进去;push数字、小数点时就不push分隔符。

我想其他细节应该你可以解决!

Sample Code:

char char_RPE[1200] = {0};   // RPE=Reverse Polish Expression; 转换后的逆波兰表达式;字符类型
char stack[1200] = {0};      //存放符号的stack
int top_stack = 0;    // stack 指针
int top_char_RPE = 0; // RPE指针
top_char_RPE = push(char_RPE, top_char_RPE, '|'); //push'|'作为分隔符,分割数字与符号,头尾都有分隔符
top_char_RPE = push(char_RPE, top_char_RPE, pop(stack));

2.3.3 类型转换

在计算时,你一定注意到了计算机不可能拿着字符去计算,所以我们还需要把分割好的词句变为double。

在前面的基础上,我们可以通过分隔符获得相应的字符串,然后通过这个式子

double_RPE[i] = strtod([分割后的一个字符串], NULL)进行转化!

P.S. strtod的具体功能与语法请自行搜索

但是,这样转换就会带来问题,操作符进行 strtod后便会变成0.00000!这无疑影响我们后续计算!

所以,我们引入辅助数组helper[ ]来判断这个字符串是数字还是操作符。

Sample Code:

int helper[1200] = {0};             //辅助数组 用来判断double_RPE中的数字与符号
if ((char_RPE[i] >= '0' && char_RPE[i] <= '9') || (char_RPE[i] == '.'))
{get_string[m] = char_RPE[i];m = m + 1;helper[j] = 1;
}
if (char_RPE[x] == '+')
{helper[i] = 2;}
We wish to get:
double_RPE[120]={1.3,4.5,0.00,3.1,6,0.00,0.00};helper[120]={1  ,1  ,2   ,1  ,1,3   ,5};//2~5分别表示加减乘除乘方

2.3.4 计算

2.3.4.1. 基本准备

为了贯彻栈的“先进后出”的思想,我们不妨先把得到的double_RPE与对应的helper逆序排列。同时,我们准备好计算时的缓冲栈load_cal_stack与计算栈cal_stack.

double RPE_Reverse[120] = {0.0};                        //逆序RPE 便于计算
double helper_Reverse[120] = {1.0};            //逆序辅助数组 便于计算
double load_cal_stack[120] = {0.0};         //缓存栈 用于存储运算后的结果
double cal_stack[120] = {0.0};                     //计算栈 用于运算
int top_cal_stack = 0;                                     //计算栈指针
int top_load_stack = 0;                                    //缓冲栈指针
int top_helper_and_RPE = MAX_DOUBLE_RPE;       // double型逆波兰指针

不难发现,我们眼前共有四个数组/栈,然后我们手里有三个“指针”(top),即表示数组中元素坐标的index.

其中top_helper_and_RPE同时控制RPEhelper,

top_cal_stack控制cal_stack,

top_load_stack控制load_stack

2.3.4.2 弹栈入栈

其实就是把2.3.1中的过程以栈的形式体现出来,可参考如下代码:

Sample code

//将double RPE的元素弹入缓冲栈
top_helper_and_RPE = pop_d(RPE_Reverse, top_helper_and_RPE);
top_load_stack = push_d(load_stack, top_load_stack, out);
//以“+”为例:
//'+' 碰到运算符后从缓冲栈中弹三个进入计算栈开始算 算完了弹回缓冲栈 后面同理
if (helper[top_helper] == 2)
{for (int i = 0; i < 3; i++){top_load_stack = pop_d(load_stack, top_load_stack);top_cal_stack = push_d(calculate_stack, top_cal_stack, out2);}
double sum = 0;
sum = cal_stack[top_cal_stack - 1] + cal_stack[top_cal_stack];
}

最后load_stack剩下的那个数就是最后结果啦!

2.3.4.3 负数处理

虽然我们已经能够很好地转化并计算一些逆波兰表达式,然而逆波兰表达式先天性存在一个小bug——他不支持负数的运算。按照我们之前的算法,碰到负数,例如-1时,我们会把他分割为"-","1"而不是"-1". 因此在计算时碰到负数会出现缺少一个操作数的情况,从而无法获得结果。所以,我们需要对式子中的负数进行预处理。

这个问题确实令人烦躁,但是静下心来仔细思考我们会发现,所谓负数无非是这两种情况:

  1. 第一个字符为减号

  2. 括号内第一个字符是减号

那么我们只需分类讨论,逐个击破即可。在解决过程中,我们的目标是让负数的运算能够成立,也即解决“操作数少一个”的问题,因此我们考虑“补上一个操作符0”使得式子能够运算。换言之,我们可以把"-1+2"变为"0-1+2"

思路已经给出,具体地解决那么就靠你的代码了!

Sample Code

char temp0_1[256] = {0};
for (int j = 0; j < len; j++){if (infix[i][j] == '('){ //处理括号后有减号的情况for (int s = t + 1; s < len; s++){if (infix[i][s] == '-'){ for (int k = t + 1; k < len; k++){temp0_1[k] = infix[i][k];//复制到temp0_1}for (int k = t + 1; k < len; k++){infix[i][k + 1] = temp0_1[k];//将数据后移,空出第一个位置}infix[i][t + 1] = '0';            //补0break;                    }}}

3. 变量处理

恭喜你!你已经能够成功计算简单的数学算式了!然而接下来你还需要处理变量。变量可能出现在等式左边或者右边,并且具有不同的含义,因此我们需要对变量进行一些定义。

Lab4给出了用数组的方式进行定义,其本质上是三组信息:

int variables[20]                //1.变量编号
char var_names[20]          //2.变量名称
double var_values[20]       //3.变量值

然而我们发现,这三组信息分别有着三个不同的数据类型,之后调用与处理的时候绝对会让你抓狂。To make your life easier, 我们引入另一种数据类型:

结构体 struct

3.1 结构体struct

如果你有一定编程基础,struct可以类比为其他语言中的class,或者是Matlab中的cell。结构体的优点在于可以容纳各种类型的数据,将某一个数据的各种不同类型的信息组合在一起形成一个整体的“结构”。看下面的例子:

struct Student{  char name[50];          //姓名  int num;              //学号  char sex;             //性别  double score;     //成绩
};我们一般这样调用:
int main(){struct Student David;David.num = 114514;printf("num:%d\n",David.num);return 0;
}

这样我们就定义好了Student的结构,我们可以非常轻松的调用struct中国的各种类型的数据。这种定义方式虽然没有问题,在中文互联网十分常见,但是每当我们要调用的时候就要struct XXX,非常麻烦不好看,因此我们普遍采用下面的方法调用:

typedef struct
{char name[50];int num;char sex[5];double score;
}Students;int main(){Students David; //不需要在前面写struct了,简洁清楚.
}

所以很自然的,我们可以定义struct Variables来实现我们对变量的操作:

Sample Code

typedef struct
{char name[120];double value;
} variablesint main(){Variables variables[20];              //结构体可以组成一个数组variables[0].name = "abc";          //第一个variables结构体的name是abcvaribales[0].value = 114.514;        //第一个variables结构体的value是114.514
}

结构体的其他操作可以参考视频:https://youtu.be/dqa0KMSMx2w

3.2 等号左边的变量

对于一个等式,等号左边的变量应该是被赋值的变量,并且这个变量的值可以在之后被调用。我们首先需要将等式左边的变量名存储到结构体name中,最后把算好的值返回给结构体中的value。在计算时,还需要识别等式右边的变量并找出他对应的值。

对于等号左边的变量,我们可以非常容易的把他作为字符串提取出来然后写入variables.name,你可能需要用到这个函数:

sprintf([写入的目标], [被写入的内容], "abc"); //"abc"告诉计算机写入的格式。

具体语法请自行搜索。

最后我们将计算得到的结果赋值给他就好了!

variables[i].value = [FinalAnswer];

3.3 等号右边的变量

显然,我们需要识别右边的变量并获得其对应的数值,然后返回给用于计算的double_RPE,然而这个过程看似简单,实则困难重重,请做好debug的心理准备。

要实现识别的操作,我们需要对2.3.3中的代码进行升级

Sample Code

if ((char_RPE[x] >= '0' && char_RPE[x] <= '9') || (char_RPE[x] == '.') || (char_RPE[x] >= 'a' && char_RPE[x] <= 'z')){temp[m] = char_RPE[x];m = m + 1;helper[l] = 1;
}
//变量的识别与转化(难点)由于temp中可能因为各种原因而没有元素导致之后strcmp失效,所以采取temp赋值和空字符判断
int a = 0;
int var_position;
for (int t = 0; t < i; t++){if (strcmp(temp, variable[t].var_name) == 0)a = a + 1;//变量判断var_position = t;if (strcmp(temp, variable[t].var_name) != 0)continue;
}if (a >= 1)   double_RPE[y] = variable[var_position].var_value;y = y + 1;
//找到了之前的变量 采用之前存储的数据if (a == 0)double_RPE[y] = strtod(temp, NULL);y = y + 1;//没找到 一定是数字(如果是新变量则不合法)

我们使用strcmp函数进行字符串的比较,用来查找右边变量对应的数值。注意,改代码直接复制不可能运行,你需要理解每一步后自己仿照这敲出来!

如果你发现结果完全不符合预期,强烈建议你使用printf来观察计算机到底在算写什么。难点在于后面四个if的条件应该怎么写。

Sample Code

printf("temp:%s     variable:%s\n",temp,variable[t].varname);
printf("True of Flase: %d\n", a);

4. 终章

如果你勤于动脑,勤于动手打代码,勇于运行调试,那么到这里你应该能基本上有个比较漂亮的输出了。虽然很有可能你的结果还有错误,你的joj还告诉你runtime error,但你先别急,仔细检查你的while循环、if语句判断条件、你的top值,通过printf来观察计算机的行为并且找出错误!这一定非常煎熬,但是只要be patient,就一定可以找出对应的错误。

这里直接给出测试点:

Sample Test

a = 1 + 1
b= 10^(-2)
c =(0 /a+ 0 / 1+ 0/3 + 0/a + b * 2.5)* b^(-2)
d=a^( a^  2) /(4*8)
e   =-8+ (6+c)^1+3.04/(3.04^(a*d)* d)
a=    b*c + d - e + e^(b^(-1) / 50)
f = (2.5^( -2) -(-23.2)/(5.8)^(b^0) - 0.16)^(a ^ 0+b ^ 0+e ^ 0)
a = (((200*b)^2)^(1+f^(e^0/3)/2)*60/2+1)*3+10624/f^0.5
tenlengthv=-3.2+(8-2^2) + 1^100/((2^0.5)* (4^0.25)) + (-0.25)+ 1 - 0.6 / 0.4 + 16^ (1/0.25/16)
result1234=((((((a-7081)))))-(((( ((b^(-2)))))))*(((( (((1))))))-((((((0)))) )))/(((((-1)^2))) * ((((-2)^2))) ))
ans = result1234 * b + tenlengthv

如果这些测试点能够通过,最后加上分号的功能即可!

最后给出完整代码,如有不足欢迎指正!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>#define MAX_LINES 120
#define MAX_LEN 120#define MAX_NUM_VARIABLES 20
#define MAX_NAME_LENGTH 15char out1 = 0;
double out2 = 0;int push(char *stack, int top, char element);       // push char
int push_d(double *stack, int top, double element); // push double
int pop(char *stack, int top);                      // pop char
int pop_d(double *stack, int top);                  // pop double
int precedence(char input);                         // 比较优先级
int isEmpty(int top);typedef struct
{char var_name[120];double var_value;
} Variables;//定义变量结构体int main()
{/*读取文件*/Variables variable[200];int line = 0;char infix[MAX_LINES][MAX_LEN];FILE *file;file = fopen("commands.txt", "r");while (!feof(file) && !ferror(file)){if (fgets(infix[line], MAX_LEN, file) != NULL)line++;}fclose(file);for (int i = 0; i < line; i++){char char_RPE[1200] = {0};   // RPE=Reverse Polish Expression; 转换后的逆波兰表达式;字符类型char stack[1200] = {0};      //存放符号的stackchar rubbishbin[1200] = {0}; //垃圾桶 处理多余的括号char position[1200] = {0};char temp0[256] = {0};char Temp1[256] = {0};char Temp2[256] = {0};double double_RPE[1200] = {0.0};         // RPE double类型double double_RPE_Reverse[1200] = {0.0}; //逆序RPE 便于计算double helper[1200] = {1.0};             //辅助数组 用来判断double_RPE中的数字与符号double helper_Reverse[1200] = {1.0};     //逆序辅助数组 便于计算double load_stack[1200] = {0.0};         //缓存栈 用于存储运算后的结果double calculate_stack[1200] = {0.0};    //计算栈 用于运算int top_stack = 0;    // stack 指针int top_char_RPE = 0; // RPE指针int top_rubbishbin = 0;int len = 0;int equal_position = 0;int semi = 0;len = strlen(infix[i]); //获得每一行式子长度/*预处理1:解决变量问题*/for (int j = 0; j < len; j++){if (infix[i][j] == ';'){semi = 1;}}//分号处理for (int j = 0; j < len; j++){if (infix[i][j] == '='){equal_position = j;}}//识别等号for (int m = 0; m < equal_position; m++){Temp2[m] = infix[i][m];}sprintf(variable[i].var_name, Temp2, "abc"); //导入变量名for (int j = 0; j < len; j++){Temp1[j] = infix[i][j];infix[i][j] = '0';}int l = 0;for (int k = equal_position; k <= len + equal_position; k++){infix[i][l++] = Temp1[k];}//删去等式左边 保留右边表达式/*预处理2:解决字符中负数问题*/for (int j = 0; j < len; j++){if (infix[i][j] == '-'){for (int j = 0; j < len; j++)temp0[j] = infix[i][j]; for (int j = 0; j < len; j++)infix[i][j + 1] = temp0[j]; infix[i][0] = '0';         }break;}char temp0_1[256] = {0};for (int p = 0; p < len; p++){if (infix[i][p] == '('){ for (int r = p + 1; r < len; r++){if (infix[i][r] == '-'){ for (int m = p + 1; m < len; m++)temp0_1[m] = infix[i][m];for (int m = p + 1; m < len; m++)infix[i][m + 1] = temp0_1[m];infix[i][p + 1] = '0';}break;                    }}}/*逆波兰表达式的转换*/for (int j = 0; j < len; j++){// 数字 立刻输入到RPE栈if ((infix[i][j] >= '0' && infix[i][j] <= '9') || (infix[i][j] == '.') || (infix[i][j] >= 'a' && infix[i][j] <= 'z')){if (isEmpty(top_char_RPE) == 0){top_char_RPE = push(char_RPE, top_char_RPE, ','); //','作为分隔符,分割数字与符号,头尾都有分隔符}top_char_RPE = push(char_RPE, top_char_RPE, infix[i][j]);}// push '(',直到')'再输出if (infix[i][j] == '('){top_stack = push(stack, top_stack, infix[i][j]);}//‘)’pop所有元素直到'('if (infix[i][j] == ')'){while (isEmpty(top_stack) >= 1 && stack[top_stack - 1] != '('){top_stack = pop(stack, top_stack);top_char_RPE = push(char_RPE, top_char_RPE, ','); // 碰到符号push分隔符top_char_RPE = push(char_RPE, top_char_RPE, out1);}// if (isEmpty(top_stack) == 0)//     top_stack = pop(stack, top_stack);top_stack = pop(stack, top_stack);}//加减乘除if (infix[i][j] == '+' || infix[i][j] == '-' || infix[i][j] == '*' || infix[i][j] == '/' || infix[i][j] == '^'){if (isEmpty(top_stack) == 0){top_char_RPE = push(char_RPE, top_char_RPE, ',');top_stack = push(stack, top_stack, infix[i][j]);continue;}while (isEmpty(top_stack) >= 1 && precedence(stack[top_stack - 1]) >= precedence(infix[i][j])){top_stack = pop(stack, top_stack);top_char_RPE = push(char_RPE, top_char_RPE, ',');top_char_RPE = push(char_RPE, top_char_RPE, out1);}top_char_RPE = push(char_RPE, top_char_RPE, ',');top_stack = push(stack, top_stack, infix[i][j]);}}//最后清空栈中残余的元素while (isEmpty(top_stack) >= 1){top_char_RPE = push(char_RPE, top_char_RPE, ',');top_stack = pop(stack, top_stack);if (out1 == '('){top_char_RPE = pop(char_RPE, top_char_RPE);top_rubbishbin = push(rubbishbin, top_rubbishbin, out1);}else{top_char_RPE = push(char_RPE, top_char_RPE, out1);}}top_char_RPE = push(char_RPE, top_char_RPE, ',');// RPE测试点// printf("%s\n", char_RPE);/*将字符串的逆波兰表达式变为double型 便于小数运算*/int len_char_RPE = 0;len_char_RPE = strlen(char_RPE);int n = 0;int y = 0;for (int k = 0; k < len_char_RPE; k++){if (char_RPE[k] == ','){position[n++] = k; //确定分隔符位置}}for (int l = 0; l < n; l++){int m = 0;char temp[1200] = {0};for (int x = position[l] + 1; x < position[l + 1]; x++) //获得每两个分隔符之间的字符 并转换为double{if ((char_RPE[x] >= '0' && char_RPE[x] <= '9') || (char_RPE[x] == '.') || (char_RPE[x] >= 'a' && char_RPE[x] <= 'z')){temp[m] = char_RPE[x];m = m + 1;helper[l] = 1;}//与此同时 使用辅助数组判断字符是否为运算符号if (char_RPE[x] == '+'){temp[0] = 'Z';helper[l] = 2;}if (char_RPE[x] == '-'){temp[0] = 'Z';helper[l] = 3;}if (char_RPE[x] == '*'){temp[0] = 'Z';helper[l] = 4;}if (char_RPE[x] == '/'){temp[0] = 'Z';helper[l] = 5;}if (char_RPE[x] == '^'){temp[0] = 'Z';helper[l] = 6;}}//变量的识别与转化(难点)由于temp中可能因为各种原因而没有元素导致之后strcmp失效,所以采取temp赋值和空字符判断int a = 0;int var_position;for (int t = 0; t < i; t++){if (temp[0] != 'Z' && temp[0] != '\0' && strncmp(temp, variable[t].var_name, m) == 0){a = a + 1;//变量判断var_position = t;}if (strncmp(temp, variable[t].var_name, m) != 0){continue;}}if (a >= 1 && temp[0] != 'Z' && temp[0] != '\0')   {double_RPE[y] = variable[var_position].var_value;y = y + 1;}//找到了之前的变量 采用之前存储的数据if (a == 0){double_RPE[y] = strtod(temp, NULL);y = y + 1;}//没找到 一定是数字(如果是新变量则不合法)}int top_cal_stack = 0;      //计算栈指针int top_double_RPE = y - 2; // double型逆波兰指针int top_load_stack = 0;     //缓冲栈指针// 逆序,便于计算for (int i = 0; i < y - 1; i++){double_RPE_Reverse[y - 2 - i] = double_RPE[i];helper_Reverse[y - 2 - i] = helper[i];}for (int i = 0; i < y - 1; i++){double_RPE[i] = double_RPE_Reverse[i];helper[i] = helper_Reverse[i];}/*借助辅助数组与缓冲栈 开始计算*/for (int i = 0; i < y - 1; i++){//将double RPE的元素弹入缓冲栈top_double_RPE = pop_d(double_RPE, top_double_RPE);top_load_stack = push_d(load_stack, top_load_stack, out2);//'+' 碰到运算符后从缓冲栈中弹三个进入计算栈开始算 算完了弹回缓冲栈 后面同理if (helper[y - 2 - i] == 2){for (int i = 0; i < 3; i++){top_load_stack = pop_d(load_stack, top_load_stack);top_cal_stack = push_d(calculate_stack, top_cal_stack, out2);}double sum = 0;sum = calculate_stack[top_cal_stack - 1] + calculate_stack[top_cal_stack];for (int i = 0; i < 3; i++){top_cal_stack = pop_d(calculate_stack, top_cal_stack);}top_load_stack = push_d(load_stack, top_load_stack, sum);}//'-'if (helper[y - 2 - i] == 3){for (int i = 0; i < 3; i++){top_load_stack = pop_d(load_stack, top_load_stack);top_cal_stack = push_d(calculate_stack, top_cal_stack, out2);}double diff = 0;diff = calculate_stack[top_cal_stack] - calculate_stack[top_cal_stack - 1];for (int i = 0; i < 3; i++){top_cal_stack = pop_d(calculate_stack, top_cal_stack);}top_load_stack = push_d(load_stack, top_load_stack, diff);}//'*'if (helper[y - 2 - i] == 4){for (int i = 0; i < 3; i++){top_load_stack = pop_d(load_stack, top_load_stack);top_cal_stack = push_d(calculate_stack, top_cal_stack, out2);}double multiply = 0;multiply = calculate_stack[top_cal_stack - 1] * calculate_stack[top_cal_stack];for (int i = 0; i < 3; i++){top_cal_stack = pop_d(calculate_stack, top_cal_stack);}top_load_stack = push_d(load_stack, top_load_stack, multiply);}//'/'if (helper[y - 2 - i] == 5){for (int i = 0; i < 3; i++){top_load_stack = pop_d(load_stack, top_load_stack);top_cal_stack = push_d(calculate_stack, top_cal_stack, out2);}double quotient = 0;quotient = calculate_stack[top_cal_stack] / calculate_stack[top_cal_stack - 1];for (int i = 0; i < 3; i++){top_cal_stack = pop_d(calculate_stack, top_cal_stack);}top_load_stack = push_d(load_stack, top_load_stack, quotient);}//'^'if (helper[y - 2 - i] == 6){for (int i = 0; i < 3; i++){top_load_stack = pop_d(load_stack, top_load_stack);top_cal_stack = push_d(calculate_stack, top_cal_stack, out2);}double power = 0;power = pow(calculate_stack[top_cal_stack], calculate_stack[top_cal_stack - 1]);for (int i = 0; i < 3; i++){top_cal_stack = pop_d(calculate_stack, top_cal_stack);}top_load_stack = push_d(load_stack, top_load_stack, power);}else{continue;}}if (semi != 1){printf("%f\n", load_stack[top_load_stack]);}variable[i].var_value = load_stack[top_load_stack];} // 一行循环结束return 0;
}int push(char *stack, int top, char input)
{stack[top++] = input;return top;
}int pop(char *stack, int top)
{if (top == 0){return 0;}out1 = stack[--top];return top;
}int precedence(char input)
{if (input == '(')return 1;if (input == '+' || input == '-')return 2;if (input == '*' || input == '/')return 3;if (input == '^')return 4;elsereturn 0;
}int isEmpty(int top)
{if (top == 0)return 0;elsereturn 1;
}int push_d(double *stack, int top, double input)
{stack[++top] = input;return top;
}int pop_d(double *stack, int top)
{out2 = stack[top--];return top;
}

C语言实现简单解释器(interpreter)相关推荐

  1. 自定义语言的实现——解释器模式

    本文转载自 :http://blog.csdn.net/lovelion/article/details/7713567 有朋友一直在等待我的解释器模式文稿,,现把某个版本发在博客上,欢迎大家讨论! ...

  2. mysql解释器_atitit.java解析sql语言解析器解释器的实现

    atitit.java解析sql语言解析器解释器的实现 1. 解析sql的本质:实现一个4gl dsl编程语言的编译器 1 2. 解析sql的主要的流程,词法分析,而后进行语法分析,语义分析,构建sq ...

  3. 设计模式学习笔记——解释器(Interpreter)模式

    设计模式学习笔记--解释器(Interpreter)模式 @(设计模式)[设计模式, 解释器模式, Interpreter] 设计模式学习笔记解释器Interpreter模式 基本介绍 解释器案例 类 ...

  4. 【软件开发】Java语言的简单介绍

    Java语言的简单介绍 一.Java语言的介绍 二.Java的版本 三.JDK的介绍 四.Java API文档 五.Java语言的特点 1. 面向对象 2. 解释性 3. 多线程 4. 可移植性 / ...

  5. python和c 的区别-Python和C语言的区别是什么?Python和C语言的简单比较

    Python和C语言都是一种可用于实现多线程的高级编程语言,那么它们之间有什么区别?本篇文章就来简单比较一下Python和C语言,让大家了解Python和C语言之间的区别,希望对大家有所帮助. 什么是 ...

  6. 实用c语言函数源码,C语言编写简单朗读小工具(有源码)

    原标题:C语言编写简单朗读小工具(有源码) 最近不少人在后台留言说学C都是面对枯燥的控制台程序,能不能体现一下C语言的实际用途,今天我们就理论结合实践一把:C语言结合VBS脚本编写一个简单的朗读小工具 ...

  7. c语言英汉互译编程,用C语言编辑简单英汉互译词典.doc

    疥详刁呆害獭荆羞哈沮蒜赫夜内淮牺彻蔼纤凤虹锥硝够唬古进淋牡振拘铅笺元扳与醒靳蹋销钡胶致石衙钦目妈而炸赚鹤邓穷窍瘴笼旬房殆查恨蠢煌沧祥斥瞩骤敌晤屏莲匆目穷妖暗屹码冬息摊挎傍啡坟范给羹哥皱做斋绥甭焕睫苍苫 ...

  8. Java语言实现简单FTP软件------源码放送(十三)

    Java语言实现简单FTP软件------>FTP协议分析(一) Java语言实现简单FTP软件------>FTP软件效果图预览之下载功能(二) Java语言实现简单FTP软件----- ...

  9. perl语言入门第七版中文_python和c语言哪个简单

    python相较C语言入门要简单的多. C语言是一门面向过程.抽象化的通用程序设计语言,广泛应用于底层开发.C语言能以简易的方式编译.处理低级存储器. C语言是仅产生少量的机器语言以及不需要任何运行环 ...

最新文章

  1. ip协议分析实验报告_入门工业通讯之EtherNet/IP协议分析
  2. 回文树(回文自动机) - URAL 1960 Palindromes and Super Abilities
  3. php面试题2018
  4. 本周开课 | 第 5 期全基因组/外显子组家系分析理论和实战
  5. 超分辨率技术,随机噪声
  6. 写你自己的MVC框架
  7. TensorFlow 图片预处理
  8. macOS Sierra 10.12.6 odoo 10.0 开发环境配置
  9. 金蝶K3系统定制国际销售日报表
  10. win10显示未安装任何音频输出设备问题解决
  11. 【Android实习】20场面试斩获大厂offer,我学会了什么
  12. 瑞芯微(RKDocs)平台技术开发资料汇总(rk3188,rk3066,RK3128,rk3288)
  13. Android Studio App设置背景图片
  14. 记一次酷狗音乐API的获取,感兴趣的可以自己封装开发自己的音乐播放器
  15. BGP------BGP工作原理、BGP属性及选路原则
  16. 学习强国:我国自研统信软件操作系统有了“终端管家”
  17. 【Pandas】Pandas数据分类
  18. recv/send堵塞和非堵塞
  19. 操作记录-2020-11-08:精简代码处理RNA_seq数据
  20. 教育部公布研考违规违法行为举报电话

热门文章

  1. android camera surface不显示图像问题
  2. 计算机图形学 - surface,layer,pipeline
  3. 劫匪深夜打劫女汉子 被高跟鞋猛踹肚子
  4. 为什么采用达龙平台实施桌面虚拟化更合适?
  5. 超过2t硬盘分区_磁盘挂载问题:Fdisk最大只能创建2T分区的盘,超过2T使用parted...
  6. 方法——检查参数的有效性
  7. shell编程(练习笔记)
  8. 开源文档系统php,推荐几种常见的PHP开源文档管理系统
  9. 图的深度优先和广度优先
  10. 微信小程序之输入框文本的获取与展示