HinM_COMPILER_cale计划和实现

本文探讨了如何利用编译原理中的概念实现简单及复杂的计算器。通过DFA方法完成基本四则运算,并借助yacc和lex处理更复杂的表达式。

  

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的,既然是这样,就可以更好地将代码移植过去。对这方面的知识我进行了学习后积累了以下知识:
内容概要:本文档围绕“经济学期刊论文复现:数字化转型能否促进企业的高质量发展”这一核心命题,系统整合了MATLAB与Python编程实现的大量科研案例,聚焦于数字化转型对企业全要素生产率(TFP)及高质量发展影响的实证研究。文档不仅复现了高水平经济学期刊论文中的计量经济模型,如基于中国上市公司数据的数字化转型与生产率关系分析,还深度融合了工程领域的建模技术,涵盖微电网优化、负荷预测、风电光伏不确定性建模、电力系统故障仿真等。同时,提供了智能优化算法(如遗传算法、粒子群优化)、机器学习(LSTM、CNN-BiGRU-Attention)、信号处理、路径规划等多学科交叉的技术资源,构建了一个从理论推导到代码实现的完整科研支持体系,旨在帮助研究者系统掌握论文复现与实证分析的核心方法。; 适合人群:具备一定MATLAB或Python编程基础,从事经济学、管理学、能源系统、智能制造及相关交叉学科研究的研究生、科研人员及高校教师。; 使用场景及目标:①复现经济学顶刊中关于数字化转型与企业高质量发展的实证模型;②学习如何量化数字化转型并构建其对企业绩效的影响评估框架;③掌握基于真实数据的计量经济建模、场景生成与优化调度仿真技术,全面提升科研论文写作与实证研究能力。; 阅读建议:建议读者结合文中提供的代码与数据资源,重点研读“论文复现”与“创新未发表”模块,按照技术路径循序渐进地实现模型复现与拓展。推荐关注“荔枝科研社”公众号及百度网盘链接获取完整资料,系统性地开展学习与科研实践。
下载代码方式:https://pan.quark.cn/s/9de6a9d0b3d8 依据所提供的文件内容,能够推导出此段程序的核心任务在于对一个任意的三位数进行拆解,并且分别呈现该数值的百位、十位及个位部分。随后,我们将对该知识点进行进一步的深入研究。 ### 一、程序功能说明 #### 1. 接收任意一个三位数输入 程序起始阶段运用`scanf`函数来获取用户输入的一个整数。为确保输入内容确实为一个三位数,在实际应用场景中通常需要嵌入验证机制来保障输入的有效性。然而,在本示例情形下,该环节被简化处理,预设用户总会准确输入一个三位数。 #### 2. 实施数字的拆分并提取各位置数值 程序借助一系列数学计算来对三位数进行拆分,将其转化为百位、十位个位三个独立的构成部分。具体而言,通过除法取模运算完成了这一过程。 #### 3. 展示各位置上的数值 程序运用`printf`函数来输出原始数值以及各个位上的数值。需要留意的是,代码中的输出部分似乎存在一些混淆,存在语法上的错误,例如多余的`printf`语句乱码字符等问题。 ### 二、核心代码分析 #### 1. 数字拆分逻辑 ```c a[0] = n / 1000; // 提取千位数,但鉴于题目要求是三位数,此处应为百位数 a[1] = n % 1000 / 100; // 提取百位数 a[2] = n % 1000 % 100 / 10; // 提取十位数 a[3] = n % 1000 % 100 % 10; // 提取个位数 ``` 这段代码通过一连串的除法取模运算,成功地将输入的数字n拆分为百位、十位个位三个独立的构成部分,...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值