HinM_COMPILER_cale项目的计划和实现
HinM_COMPILER_cale项目是在学习编译原理的时候结合实际写出的子项目,这里讨论的是用编译的思想来实现一个高效能的计算器。
项目分为两个部分,一个是采用DFA的方式生成一个简单的四则运算的计算器。第二个部分是结合使用yacc和lex生成一个复杂的计算器。项目实现时间计划为一个星期。(2008-6-16—2008-6-22)
一个采用DFA的方式生成的简单的四则运算的计算器
这个项目我的实现参考了别人的例子,所以还是比较顺利的。
DFA的意思就是有穷自动机,是词法分析的内容,其实用词法分析的方法编写这个程序对于我自己来说教益非常大,可以说获得了智慧。我感觉如果我们把机器的状态用更加形式化的方法表示出来,将更容易控制。这个简单的,只可以处理四则运算的计算器的DFA如下:

态涵涵盖了简单的四则运算计算器可能出现的所有状态,首先能够在头脑中将所有的状态思考出来,然后用图表或其它抽象的方法将其形式化表现,用程序语言叙述,这是计算机程序编写的基本过程。

这样,就可以来编写代码,实现这个DFA,比较简答的实现方法是两层select的嵌套
Select(0)
{
Case 1……
Case 2……
Case 3
{
                Select(input)
{
Case GOTO1……
Case GOTO2……
Case GOTO3……
……
}
……
}
这样正好对于了状态和状态的转换,而且DFA又只是双层的,比较贴切,这个只是也是积累到的;另一个方面,比较重要的就是对于非常的输入DFA可以不相应,因为本来在这个输入下DFA本身就没有合适的状态来转移嘛.当然,不响应是对于计算器这个程序的特性来说的,对于其它不同的程序,可以统一导向到一个错误收集区域中去,这样整个程序的架构就合理了。
示例代码采用c+win32api实现,采用api的好处是以后移植到HinM中去的时候,修改相应的api就可以了。下面分析代码
// 各函数参数表含义见其定义处/
//
// DFA模拟函数//     
void MiniDFA(HWND hWnd, UINT uInput);//
// 向显示区追加一位数字//
void AppendNumber(HWND hWnd, TCHAR ch);//
// 在显示区退格删去一位数字//
void BackSpace(HWND hWnd);//
// 改变显示区数字的符号//
void ChangeSign(HWND hWnd);//
// 显示数字//
void ShowNumber (HWND hWnd, double dwNumber);//
// 返回显示的浮点数//
double DisplayToNumber(HWND hWnd);//
// 对两个操作数做加减乘除运算,返回运算结果//
double CalcIt(double dwLeftNum, UINT uOperation, double dwRightNum);//
// 显示区清屏//
void Clear(HWND hWnd);//
// 显示当前运算符//
void SetOperator(HWND hWnd, UINT uOperator);//
// 显示记忆标志//
void SetMemoSign(HWND hWnd, BOOL bSet);//
//
这里就是这个minicale中用到的一些函数了,可以看到比DFA那个图中的要实际一些,包括下面几个类别
1.       主函数,程序入口 MiNiDFA
2.       数据运算 CalcIt
3.       对当前数据的增加,删除,修改 AppendNumber,BackSpace,ChangeSign
4.       显示存储数据 ShowNumbe,DisplayToNumber,SetOperator,SetMemoSign
5.       其它 Clear
其实只有前面三个比较重要,后面两个不一定需要成为函数。
下面从细节到整体分析程序
//AppendNumber 追加显示数字
// hWnd编辑控件所在窗口句柄
// ch读取的数字或小数点
void AppendNumber(HWND hWnd, TCHAR ch)
{
TCHAR szBuffer[40], // 用于接收当前显示数字串
szAppend[2] = {'0', '/0'}; // szAppend[0] 为要添加的数字字符
GetWindowText(GetDlgItem(hWnd, IDC_EDITDISPLAY), szBuffer, 30);
szAppend[0] = ch;
strcat(szBuffer, szAppend);
SetWindowText(GetDlgItem(hWnd, IDC_EDITDISPLAY), szBuffer);
}//
函数平淡无奇,但是给我编写操作系统的提示是需要有类似于GetWindowText,SetWindowText,和GetDlgitem之类的在class和View 之间传递消息的函数,我认为这是很合理的。
//BackSpace退格,删去显示中的一位
// hWnd编辑控件所在窗口句柄
void BackSpace(HWND hWnd)
{
TCHAR szBuffer[40]; // 用于接收当前显示数字串
int length;
length = GetWindowText(GetDlgItem(hWnd, IDC_EDITDISPLAY), szBuffer, 30);
szBuffer[length - 1] = '/0';
}
//ChangeSign 改变符号
// hWnd编辑控件所在窗口句柄
void ChangeSign(HWND hWnd)
{
TCHAR szBuffer[40]; // 当前显示数字串
int length;
length = GetWindowText(GetDlgItem(hWnd, IDC_EDITDISPLAY), szBuffer, 30);
if(TEXT('0') == szBuffer[0])
return; //无数,所以不变
if(TEXT('-') == szBuffer[0]) // 负数
szBuffer[0] = TEXT(' ');
else // 正数 ,它的length被减小了1
{
strrev(szBuffer);
if(TEXT(' ') == szBuffer[length - 1])
length -= 1;
szBuffer[length] = TEXT('-');
szBuffer[length + 1] = TEXT('/0');
strrev(szBuffer);
}
SetWindowText(GetDlgItem(hWnd, IDC_EDITDISPLAY), szBuffer);
}
计算函数
double CalcIt(double dwLeftNum, UINT uOperation, double dwRightNum)
{
switch (uOperation)
{
case '+': return dwLeftNum + dwRightNum ;
case '-': return dwLeftNum - dwRightNum ;
case '*': return dwLeftNum * dwRightNum ;
case '/': return dwLeftNum / dwRightNum;
default: return 0;
}
}
 
下面是这个程序的核心,其实就是将那个图程序化的一个东西:计算器的确定有穷自动机(DFA)模拟函数
//MiNiDfa计算器的确定有穷自动机(DFA)模拟函数
// hWnd主窗口句柄
// uInput读取的输入符号
// 状态的转换 (一个状态)-输入->(另一状态)
void MiniDFA(HWND hWnd, UINT uInput)
{
static UINT uState = 0, uOperator = '=';//     状态,运算符
static double dwLeftNum = 0, dwRightNum = 0;// 左右操作数
static double dwMemo = 0; // 保存一个数的空间
static UINT uIntegralCount = 0, // 整数位数计数器
uDecCount = 0; // 小数位数计数器
// 在各状态下BackSpace, CE, C, MC, MR, MS, M+, SQRT, %, 1 / X, + / -
// 的处理较为统一, 所以先行处理以简化后面DFA主体的设计
switch(uInput)
{
………………………………
// DFA主体代码
// (OneState)-input->(AnotherState)
switch(uState)
{
case 0:
switch(uInput)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
uIntegralCount = 1;
Clear(hWnd);
AppendNumber(hWnd, uInput);
uState = 1;        // (0)-num->(1)
break;
case '.':
uIntegralCount = 0; // 整数位计数器归零
Clear(hWnd);
AppendNumber(hWnd, '0'), AppendNumber(hWnd, '.'); // 显示"0."
uState = 2;        // (0)-dot->(2)
break;
case '+':
case '-':
case '*':
case '/':
uIntegralCount = 0; // 整数位计数器归零
uOperator = uInput;
SetOperator(hWnd, uOperator);
uState = 3;       // (0)-op->(3)
break;
}
break;
// 其它的都是trival,不可能实现的。
case 1:
switch(uInput)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
uIntegralCount ++;
AppendNumber(hWnd, uInput);
uState = 1;    // (1)-num->(1)
break;
case '.':
AppendNumber(hWnd, '.');
uState = 2;  // (1)-dot->(2)
break;
case '+':
case '-':
case '*':
case '/':
dwLeftNum = DisplayToNumber(hWnd);
uOperator = uInput;
SetOperator(hWnd, uOperator);
uState = 3;    // (1)-op->(3)
break;
}
break;
case 2:
switch(uInput)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
uDecCount++;
AppendNumber(hWnd, uInput);
uState = 2;    // (2)-num->(2)
break;
case '+':
case '-':
case '*':
case '/':
uDecCount = 0; // 小数位计数器归零
dwLeftNum = DisplayToNumber(hWnd);
uOperator = uInput;
SetOperator(hWnd, uOperator);
uState = 3;    // (2)-op->(3)
break;
}
break;
case 3:
switch(uInput)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
uIntegralCount = 1;
Clear(hWnd);
AppendNumber(hWnd, uInput);
uState = 4;    // (3)-num->(4)
break;
case '.':
uIntegralCount = 0; // 整数位计数器归零
Clear(hWnd);
AppendNumber(hWnd, '0'), AppendNumber(hWnd, '.'); // 显示"0."
uState = 5;    // (3)-dot->(5)
break;
case '+':
case '-':
case '*':
case '/':
uIntegralCount = 0; // 整数位计数器归零
uOperator = uInput;
SetOperator(hWnd, uOperator);
uState = 3;   // (3)-op->(3)
break;
}
break;
case 4:
switch(uInput)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
uIntegralCount ++;
AppendNumber(hWnd, uInput);
uState = 4;   // (4)-num->(4)
break;
case '.':
AppendNumber(hWnd, '.');
uState = 5; // (4)-dot->(5)
break;
case '+':
case '-':
case '*':
case '/':
uIntegralCount = 0; // 整数位计数器归零
dwRightNum = DisplayToNumber(hWnd);
dwLeftNum = CalcIt(dwLeftNum, uOperator, dwRightNum);
ShowNumber(hWnd, dwLeftNum);
uOperator = uInput;
SetOperator(hWnd, uOperator);
uState = 3;    // (4)-op->(3)
break;
case '=':
uIntegralCount = 0; // 整数位计数器归零
dwRightNum = DisplayToNumber(hWnd);
dwLeftNum = CalcIt(dwLeftNum, uOperator, dwRightNum);
ShowNumber(hWnd, dwLeftNum);
uOperator = '=';
SetOperator(hWnd, ' ');
uState = 0;   // (4)-op->(0) ,接受状态
break;
}
break;
case 5:
switch(uInput)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
uDecCount++;
AppendNumber(hWnd, uInput);
uState = 5;    // (5)-num->(5)
break;
case '+':
case '-':
case '*':
case '/':
uIntegralCount = 0; // 整数位计数器归零
uDecCount = 0; // 小数位计数器归零
dwRightNum = DisplayToNumber(hWnd);
dwLeftNum = CalcIt(dwLeftNum, uOperator, dwRightNum);
ShowNumber(hWnd, dwLeftNum);
uOperator = uInput;
SetOperator(hWnd, uOperator);
uState = 3;   // (5)-op->(3)
break;
case '=':
uIntegralCount = 0; // 整数位计数器归零
uDecCount = 0; // 小数位计数器归零
dwRightNum = DisplayToNumber(hWnd);
dwLeftNum = CalcIt(dwLeftNum, uOperator, dwRightNum);
ShowNumber(hWnd, dwLeftNum);
uOperator = '=';
SetOperator(hWnd, ' ');
uState = 0;  // (5)-op->(0)
break;
}
break;
}
}
结合使用yacc和lex生成一个复杂的计算器
光是学习yacc就很头疼,但是谁叫它那么有用了,是吧,所以所有努力都是值得的。首先说一下为什么在复杂的情况下DFA失去作用。最为显著的就是括号,这种需要前后匹配的东西,如果采取类似DFA的方法,构造一个DFA,可能是类似于下面这个样子:
其中符号的含义:
       圆圈:状态 (同心圆代表接受状态)
箭头:状态的转换方向
       num:      输入数字
       dot:        输入小数点
( 和 ): 输入括号
       op:        输入运算符号 (+、 -、 *、 /)
       =      输入等于号
I :括号计数器
那么会存在这个问题,那就是形如 (1+2+(3+4)) 的嵌套的式子的时候,在处理中间一个式子的时候,需要对前面的计算值和符号做保存,而由于嵌套的层数未知,所以这里无法控制,需要用更强的上下文无关文法作为编程思想。
在编译原理(陈意云)的100页就提供了一个计算器的yacc代码,我现在了解到yacc是可以直接嵌入vc6的,既然是这样,就可以更好地将代码移植过去。对这方面的知识我进行了学习后积累了以下知识:

HinM_COMPILER_cale计划和实现相关推荐

  1. 2017 年总结及 2018 年计划

    概述 本文写于 2018.01.01,计划从 2017 开始有目的的进行复盘行动,所以将该文搬运到此处. ----------------------------分界线---------------- ...

  2. Waymo的激光雷达计划:进展如何?

    Waymo的激光雷达计划:进展如何? Waymo's Lidar Plan: How's It Working out? 许多自动驾驶汽车(AV)开发商一直在热烈追求激光雷达技术,这一技术之所以重要, ...

  3. (14)某工业生产部门根据国家计划的安排, 拟将某种高效率的5台机器,分配给所属的3个工厂A,B,C,各工厂在获得这种机器后,可以为国家盈利的情况如表4-10所示。

    问题描述: (14)某工业生产部门根据国家计划的安排, 拟将某种高效率的5台机器,分配给所属的3个工厂A,B,C,各工厂在获得这种机器后,可以为国家盈利的情况如表4-10所示.问:这5台机器如何分配给 ...

  4. 当统计信息不准确时,CBO可能产生错误的执行计划,并在10053 trace中找到CBO出错的位置示例...

    一.本文说明: 操作系统:rhel 5.4 x32 数据库:oracle 11g r2 x32 二.实验内容: ----创建一张jack表,并创建索引jack_ind---- 1 SQL> cr ...

  5. 2017暑期挖坑计划(持续更新中~)

    2017的暑期,是高中生涯唯一一个能称得上暑期的东西了,也是决定我OI这个坑能挖多大的关键时期! 嗯,这个暑期不能颓,一定要好好挖坑,好好补题! 既然入了坑,就努力把坑挖穿再出来! 2017.11.6 ...

  6. cron 工具 每分钟_计划任务 cron和crontab

    很多时候,我们需要定时执行某个应用或者某段脚本,在linux上,可以使用cron服务,它有点类似于Windows上的任务计划程序.这篇文章将简单介绍cron的使用. 1. crond和crontab ...

  7. linux 任务计划 权限设置,Linux系统 文件权限+计划任务+日志系统

    实验目的:熟练掌握权限相关概念和具体操作 实验环境:Red Hat Enterprise Linux Server 5.3 实验步骤: 一.文件权限 二.计划任务 三.日志 一.文件权限 (一)基本权 ...

  8. 计划任务执行php文件,linux系统下添加计划任务执行php文件方法

    在web开发过程中,经常需要设定一些定期执行的任务,比如商品定时上下架.我们以php文件为例,讲解linux下的计划任务. 方法/步骤 1.打开linux系统命令行界面. 在命令行界面中输入如下命令: ...

  9. android 高通平台有前途吗,华为鸿蒙计划要适配高通平台了,可以告别安卓搭载鸿蒙OS了?...

    鸿蒙走出这一步是可以想象到的,看来华为打造这个系统希望的结果是万物皆可盘呀,所以一开始就提出了开源,也就意味着这次是高通,下次就可以是联发科,甚至更多的手机品牌也完全就可以搭载!早期我们一直在说国产手 ...

  10. python打印万年历_python青苔计划(六)打印万年历

    青苔计划 /(ㄒoㄒ)/~~ 我这样的小白 万年历打完,感觉身体被掏空 请给我暖暖的拥抱 ╮(╯╰)╭ ----------正经的分割线---------- 任务要求 刚看到要打印万年历,我彻底懵逼了 ...

最新文章

  1. 专注,专注,还是专注
  2. 线程中使用SaveFileDialog不能弹出窗体
  3. jQuery 里面的排他思想
  4. MFC对话框控件成员变量编程熟悉 - 开发一个简单天线长度计算器
  5. Android中Activity之间的数据传递(Intent和Bundle)
  6. delphi Winsock非阻塞模式详解
  7. bzoj 1031 [JSOI2007]字符加密Cipher 后缀数组
  8. Gradle构建中:No cached version available for offline mode解决方案
  9. C++语言类的多态介绍和示例
  10. C++ STL标准模板库
  11. 【资源下载】PCtoLCD2002 下载 资源分享 OLED取模软件
  12. OpenGL读取灰度图生成地形
  13. (数据结构)树的深度/高度
  14. CodeForces 1418C Mortal Kombat Tower
  15. vb.net 教程 1-9 数组3-1 数组的合并
  16. 毕业设计-基于微信小程序的医生随访系统
  17. Android 9.0 去除锁屏界面及SystemUI无sim卡拨打紧急电话控件显示功能实现
  18. Java最全八股文(2023最新整理)
  19. mysql查询和更改时区
  20. Nancy启用跨站攻击防护(CSRF)

热门文章

  1. 【CRM】CRM开发中常用表
  2. 展锐物联网芯片V8811—全球首个PSA认证区块链解决方案
  3. java编写的山寨qq_java 山寨QQ 实现(一)
  4. OpenGL ES EGL eglCreateContext
  5. 西电软工oop面向对象程序设计实验三上机报告
  6. java jlist 添加滚动条_JList滚动条问题
  7. 虚拟机 服务器死机,VMware虚拟机开机卡死黑屏,进不了系统解决办法
  8. 操盘手“本来生活”,这样把“褚橙”卖成“励志橙”
  9. 500Illegal PORT command 错误 无法取得目录列表 8uftp
  10. elk学习中遇到的一些问题