HinM_COMPILER_cale计划和实现

  

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的,既然是这样,就可以更好地将代码移植过去。对这方面的知识我进行了学习后积累了以下知识:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值