【编译原理】计算器实现(C语言)

您所在的位置:网站首页 shell简单计算器 【编译原理】计算器实现(C语言)

【编译原理】计算器实现(C语言)

#【编译原理】计算器实现(C语言)| 来源: 网络整理| 查看: 265

【编译原理】计算器实现(C语言) 题目要求简单分析(1)读文件(2)词法分析(3)语法分析(4)后缀表达式求值 总结

这几天编译原理课留了个编写一个简单的计算器,要求能够检查错误并进行计算。其实也就是完成词法分析,语法分析,再完成一个后缀表达式的计算就好了。 这里找到了一个比较简单的方法,这里记录一下。

题目要求

在这里插入图片描述

简单分析

整个程序其实整体上大致分成四部分去做 1.读文件存储内容(这里的文本是以字符的形式存储的) 2.词法分析,把字符拆分成一个个的token 3.语法分析,对拆分的token进行分析,确定有无错误 4.后缀表达式求值,在确认格式无误后进行后缀表达式求值并输出

(1)读文件

readFile()函数完成了读入字符串到str数组中保存起来,方便后续操作,在这里处理了一个结尾是否为“ . “的错误,比较简单没什么好说的。 下面是具体函数 readFile。

unsigned long readFile(char *filePath, char str[]) { FILE *fp = fopen(filePath, "r");//打开文件 if (fp == NULL) { printf("打开文件出错,请确认文件存在当前目录下!\n"); exit(0); } unsigned long i; i = fread(str, 1, MaxLen, fp); if (i >= MaxLen) { printf("文件过大!请重新输入一个更小的文件。\n"); exit(1); } if (str[i - 1] != '.') { printf("Error, Please end up with \".\"\n"); is_error = 1; } str[i] = '\0'; fclose(fp); return i; } (2)词法分析

词法分析函数Scaner()这里的词法分析生成token,如果了解过一些编译原理词法分析的内容的话,这里的这个方法其实是有很多简化的,他其实只处理并储存了数字,标识符(变量名、int 、float) 对于其它的标点,运算符等符号其实都没有进行处理,只是将其分割但是没有保存。

这里用了一个链表把标识符保存了起来,方便后续的检测

值得一提的是为什么这里用了while循环,这个循环,巧妙地解决的空格的问题,也就是如果有多余的空格,这里会直接跳过,很巧妙。

这是 Scaner()

void Scaner() { int i, is_float = -1; while (token = *src) { src++; //词法分析开始 if (token == '.') return; else if (token == '\n') line++; else if (token == '+' || token == '-' || token == '*' || token == '/' || token == '=' || token == ';' || token == '(' || token == ')') return; //处理标识符 else if ((token >= 'a' && token = 'A' && token if (!Strcmp(current->name, name1)) { current1 = current; token = current->stamp; new_id = 0; return; } } token = Id; if (current1->type_float == 1) is_float = 1; ListPush(token, name1, -1, 0, is_float); return; } //处理整数,浮点数 else if (token >= '0' && token if (*src == '.') { float_not = 0; src++; continue; } if (float_not) { token_value = token_value * 10 + (*src - '0'); src++; } else { num = num * 10; token_value += (float)((*src - '0') * 1.0 / num); src++; } } token = Num; return; } else if (token == ' ') continue; else return; } }

后面还用match()把Scaner()进行了包装,这里的match在语法分析里有妙用。

(3)语法分析

Reader()函数做了语法分析。 这里先介绍match()的作用,就是判断给定的格式是不是当前token指向的字符格式,如果是则扫描下一个token,这里的token是指一个字符,一个数字,一个标识符。也就是说match()做的就是语法里面的匹配。

这样来再看语法分析,根据当年token的类型,决定进行什么语句的分析。 例如说开头的是变量Id,就是赋值语句,依次去match(Id)、match(=)、match(;)。这里为什么没有去求值,后面的后缀表达式介绍。 开头的是int 就要匹配定义语句:match(int)、match(=)、match(Id)、match(;)。

基本上搞清楚token指向的是什么,就很容易理解了。 Reader()

void Reader() { if (token == Id) { if (new_id) { ListPop(); printf("Line %d: Error: Undeclared Variable!\n", line); is_error = 1; } Token *assign = current; match(Id); match('='); if (current->type_float == 1) { is_typeint = 1; } float value; value = expr(); if (((value - (int)value) == 0) && is_typeint == 1) { printf("Line %d: Error: Illegal Conversion!\n", line); is_error = 1; } if (assign->type == Float) assign->value = value; else assign->value = (int)value; match(';'); } else if (token == Int) { new_id = 1; match(Int); if (!new_id) { printf("Line %d: Warning: The Variable Is Repeatedly Declared!\n", line); is_error = 1; } current->type = Int; match(Id); match(';'); }else if (token == Float) { new_id = 1; match(Float); if (!new_id) { printf("Line %d: Warning: The Variable Is Repeatedly Declared!\n", line); is_error = 1; } current->type = Float; match(Id); match(';'); } else if (token == Write) { float output; int output_type; match(Write); match('('); if (new_id) { ListPop(); printf("Line %d: Error:Undeclared Variable!\n", line); is_error = 1; } else { output = current->value; output_type = current->type; } match(Id); if (!is_var) { match(Id); } match(')'); match(';'); if (!is_error) { if (output_type == Int) printf(" %d \n", (int)output); else if (output_type == Float) printf(" %f \n", output); } } else if (token == '.') return; } (4)后缀表达式求值

后缀表达式求值本质上就是一个递归(或者堆栈)区分出运算符号优先级的过程,具体操作可以参照数据结构课本的堆栈相关章节。

这里只说这里的具体实现,因为我们在Scaner()函数重拆分的时候会得到具体的数字和运算符,而我们的Reader()又是一个个token进行匹配的,所以当我们进行计算时,可以先行计算出来值。

总结

基本内容就结束了,详细的源码以及说明放在这里了,如何运行请参照里面的。readme.md

链接: Caculator.



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3