辣鸡用C语言实现解释器(二)
文件读入方式
使用fgets函数。
函数功能为从指定的流中读取<span 数据,每次读取一行。其原型为:<span str 所指向的字符串内。当读取<span
值得一提的是,如果是读到换行符停止后,str所指向字符串倒数第二个字符为‘\n’,最后一个字符为‘\0'。
read_file函数中实现文件的逐行读入以及简单的“分拣”:
int read_file(char *filename){FILE *fp;fp=fopen(filename,"r");if(fp==NULL){// printf("不能读取文件\n");return NO;}//创建缓冲区bufferchar *buffer=NULL;buffer=(char*)malloc(sizeof(char)*100);if(!buffer){// printf("缓冲区创建失败");return NO;}// else printf("缓冲区预备\n") ;//对文件进行操作// printf("开始读取…\n");while(!feof(fp)){if(!fgets(buffer,100,fp)) break;operate_buffer(buffer);if(buffer[0]=='/'&&buffer[1]=='/')goto here;if(buffer[0]=='\n')break;// intif(buffer[0]=='i'&&buffer[1]=='n'&&buffer[2]=='t'){read_int(buffer);}// floatelse if(buffer[0]=='f'&&buffer[1]=='l'&&buffer[2]=='o'&&buffer[3]=='a'&&buffer[4]=='t'){read_float(buffer);}// stringelse if(buffer[0]=='s'&&buffer[1]=='t'&&buffer[2]=='r'&&buffer[3]=='i'&&buffer[4]=='n'&&buffer[5]=='g'){read_string(buffer);}// 条件语句else if(buffer[0]=='i'&&buffer[1]=='f'&&buffer[2]==' '&&strchr(buffer,'{')){if_func(buffer,fp);}// 循环语句else if(buffer[0]=='f'&&buffer[1]=='o'&&buffer[2]=='r'){change_buffer(buffer,fp);}// 函数 readelse if(strchr(buffer,'r')&&strchr(buffer,'e')&&strchr(buffer,'a')&&strchr(buffer,'d')&&strchr(buffer,'(')&&(!strchr(buffer,','))){func_read(buffer);}else if(strstr(buffer,"else")&&flag_for==0){}else if(strstr(buffer,"else")&&flag_for==1){while(1){fgets(buffer,100,fp);if(strchr(buffer,'}')) break;}}// 函数printelse if(strchr(buffer,'p')&&strchr(buffer,'r')&&strchr(buffer,'i')&&strchr(buffer,'n')&&strchr(buffer,'t')&&strchr(buffer,'(')){func_print(buffer);}else if(strstr(buffer,"func")){save_func(buffer,fp);}else if((!strstr(buffer,"assign"))&&strchr(buffer,')')&&strchr(buffer,'(')&&(!strstr(buffer,"read"))){use_func(buffer);}else if(strchr(buffer,'}')){}// 赋值语句else if(!strchr(buffer,'}')){operate(buffer);}here:;}// printf("读取完成\n");// 读取变量值验证// visit();if(fclose(fp)){printf("无法关闭文件\n");return NO;}free(buffer);return OK;}
operate_buffer函数原型为:
void operate_buffer(char *buffer);
用于删去开头的空格。
变量定义要求
变量名: 字母数字或$组成,不能以数字开头,最多 32 字符,如 Dian2020,$1 都是合法变量名。
变量定义:
int a,b,c; float d,e; string f; int arri[16]; |
定义后数值类型默认值 0,字符串默认值“”(空串)。
变量类型有 int 整数,float 浮点数,string 字符串。
变量不存在作用域,一旦定义全局有效。
变量类型 变量名 1[,变量名 2][,变量名 3]…[,变量名 n]; 数组定义:
数组为固定长度,下标从 0 开始,默认值同变量类型默认值。
HASH-变量储存
在头文件中,我们使用hash函数在变量名和代表变量的数值之间进行映射。鉴于文件中涉及的变量少,懒惰的我直接从网上嫖了一个hash函并且没有考虑变量重名的情况。为此我表示忏悔,承诺在下一篇发之前好好学习hash函数的操作之后写总结。
我嫖的函数是这样的(HASHSIZE定义为101):
int hash(char *s){int h=0;for(;*s;s++)h=*s+h*31;return h%HASHSIZE;}
变量结构体定义为:
struct variable{char name[33];// type=1(int),type=2(float),type=3(string)// type=11(int array),type=22(float array)int type;float x_f;int x_i;char *str_head;int *array_i;float *array_f;};
其中,char name[33]用于存放变量名,主要是为了debug时方便查看比如变量“重名”(不同变量hash值相同)的情况。
同样鉴于涉及变量不多,我们设置全局变量:
struct variable var[101]
语句处理
对于文件中变量定义式:
int a,b,c[10];
*buffer中,int是进入read_int进行变量定义的标志。对于定义非数组,只需让该变量的type=1即可;对于数组,需要type=11的同时给int数组头指针array_i分配大小为数组下标的空间。
read_int:
void read_int(char *buffer){int poi;// 只存在一个变量if(!strchr(buffer,',')){if(!strchr(buffer,'[')){// 非数组intpoi=write_name(strchr(buffer,' ')+1,strchr(buffer,';')-1);write_type_int(poi);}else{// int数组poi=write_name(strchr(buffer,' ')+1,strchr(buffer,'[')-1);// write_type_int(poi);creat_array_int(strchr(buffer,'[')+1,strchr(buffer,']')-1,poi);}}else{char *begin=NULL;char *end=NULL;begin=strchr(buffer,' ');end=strchr(buffer,',');do{poi=write_name(begin+1,end-1);write_type_int(poi);// 找下一个,*end='#';begin=end;if(!strchr(buffer,',')){end=strchr(buffer,';');poi=write_name(begin+1,end-1);write_type_int(poi);break;}else end=strchr(buffer,',');}while(1);}}
write_name函数用于将变量名写入变量结构体中,同时返回变量hash值。
write_type_int函数用于初始化变量的type。
create_array_int用于初始化数组。
float的实现与int相同,而实现string只需将字符串初始化为空串即可,不必赘述。
THE END
下次变量赋值操作!!!
话说大家有啥时间管理大法教教我呗!我最近好颓废,鬼畜区快被我看穿了。
欢迎大家批评指正!!!优化代码啊或者错误啥的。
