C语言解释器的实现--语法解析(五)

1.代码块

  代码块是由多个表达式组成的一组代码。它可以看成是以下的形式:
  {
      exp1
      exp2
      ...
  }
  它由"{"开始,由"}"结束,中间包含多条表达式,或者是控制语句。如果不是以"{"开始,那么,一个代码块就是一条表达式。在上面的章节,我们已经介绍过了,每个表达式会产生一个中间代码。它是一个链表 struct _code * ,而一个代码块,是由多个表达式组成的,所以我们将每个表达式的中间代码链表连到一起就成了代码块的中间代码了。
  如果代码块中包含控制语句,那么,我们必须做一些处理,即在代码链表中插入跳转语句,和跳转位置(Lab)。

 
2.控制语句

2.1 C语言中,控制语句有这些:
  a. if( exp ) stmt else stmt
  b. do stmt while( exp )
  c. while( exp ) stmt
  d. for( exp1; exp2; exp3 ) stmt
  e. switch( exp ) stmt
  f. goto lab
  其中,stmt表示一个代码块。我们如何为这些代码产生中间代码呢?这里还要说明的是跳转语句。比如一个if语句:
      if( exp ) stmt1 else stmt2
  那么,它的意思是,当 exp == 0 时,跳转到stmt2位置;当exp != 0的时候不做跳转,但是stmt1执行完成后要跳转到stmt2的后面。所以,这中间涉及了两个东西:跳转语句 和 跳转的位置。跳转语句我们用三种命令表示:JE、JNE、JMP,即不等于跳转,等于跳转,无条件跳转。 跳转的位置我们用Lab表示,即在代码链表中插入一个标签,供跳转语句查找要跳转的位置。
  还是上面的if语句,它产生后的代码应该是这样的:
  
  A.  if( exp ) stmt1 else stmt2 -->
        exp
        JE L1
        stmt1
        JMP L2
      L1
        stmt2
      L2
    
  其中,L1 L2分别占用代码链表的一个节点,在code_t结构体中,用lab域表示。

2.2 控制语句中的break和continue.
  在一些控制语句中,他们支持break和continue,即如果在代码块总出现break,那么他应该跳转到代码块的外面,如果是continue,那么跳转到条件语句继续执行。例如下面的do while语句:
  
  B.  do stmt while( exp ) -->
      L1:
        stmt     <-- 如果这里出现break,那么JMP L3; 如果出现continue, 那么JMP L2
      L2:
        exp
        JNE L1
      L3:
  因为在解析stmt的时候,L1,L2和L3都已经固定好了,所以,在处理break和continue的时候,跳转的LAB都已经明确,可以用参数将L2和L3传递个stmt()函数,stmt函数中解析break和continue的时候,仅仅是添加一条跳转语句。

2.3 其他控制语句的代码形式
  
  C. while( exp ) stmt -->
        JMP L2
     L1:
        stmt
     L2:
        exp
        JNE L1
     L3:
    
  D. for( exp1; exp2; exp3 ) stmt -->
        exp1
        JMP L3
     L1:
        stmt
     L2:
        exp3
     L3:
        exp2
        JNE goto L1
     L4:
  
  E. switch( exp ){
     case 1: stmt1
     case i: stmti
     default: stmt
  ...
     }
    
         exp
         selete i and jmp(L1..Ln,L) 
     Li: stmti
     L:  stmt
     LL:
     
     selete i and jmp(L1..Ln,L) 表示 如果exp的结果是i,那么跳转到Li,否则跳转到L。switch语句跟别的控制语句不一样,其他的控制语句在还没解析代码块的时候,我们就已经知道应该创建几个Lab了,所以我们可以事先创建好Lab,然后在适当的位置插入JMP语句,这个JMP语句中跳转到的Lab这时候已经确定了。但是对于switch语句,我们事先不知道case在什么地方,所以不知道"selete i and jmp(L1..Ln,L)"应该对应什么代码。所以,我们必须解析完stmt(代码块)之后才能产生代码。 具体的做法是在解析代码快的时候记录下所以的Lab,解析完成后再做相应的处理,即构造"selete i and jmp(L1..Ln,L)"代码,将它连接到中间代码的前面。
     
  F. goto Lab -->
     JMP Lab
     
     在解析goto的时候,必须将"Lab"名称转换成我们的Lab的表示形式。

 
3.局部变量的生命周期

  在一个函数中定义的变量称之为局部变量,但是局部变量有自己的生命周期,即在自己的代码块中定义的,那么它只对这个代码块的代码可见。例如有下面的代码:
    {
        int a;
        {
            int a;
        }
        printf("%d\n", a);
    }
  那么第二个a对printf语句处是不可见的。为了表示变量的生命周期,我们为每个变量加入了begin和end域,用来保存该变量对[begin,end]区间的代码是可见的。所以,这里begin,和end怎么解析是个问题,begin不难,在解析定义的时候就可以确定,但是end确实比较难,因为必须在一个代码块中结束后(即解析到"}"后),才知道end的值。所以为了确定end的值,栈在这里又被征用了。
   
    {                          <-- 代码块开始,创建一个stack1
        int a;                 <-- 解析完a,将a压入stack1, 此时 a.begin已经确定
        {                      <--     遇到"{" 递归调用解析函数,创建一个stack2
            int a;             <--     解析完a,将a压入stack2, 此时 a.begin已经确定
        }                      <--     遇到"}",表示该代码块完成,将a从stack2中pop出来,设置a.end !
                                       此时,递归调用结束。返回到上一个代码块处理函数。
        printf("%d\n", a);     <--
    }                          <-- 到"}",表示该代码块完成,将a从stack1中pop出来,设置a.end !
   
    经过上面的过程,第一个a和第二个a的begin和end值都被确定。在代码的处理过程中,我们根据变量名查找变量时,必须根据当前代码的位置,来判断位置是否属于[begin,end]区间,而不仅仅是判断变量名。


4.函数解析

  一个函数包括这几个部分:
  a. 返回值类型
  b. 形参列表
  c. 局部变量
  d. 代码块
  例如下面的函数:
    int add( int a, int b )
    {
        int c;
        c = a + b;
        return c;
    }
  那么它的返回值类型是int, 参数列表是a、b,局部变量有c, 执行代码是 " c = a + b; return c; " 。仔细观察,它其实是由函数声明和一个代码块组成的。所以解析这个函数也很简单,其实就是解析声明,得到函数名,参数列表和返回值类型。然后执行上一章节描述的解析代码块函数,得到该函数的中间代码链。


5.附

  比如有如下的代码:
  int main( int argc, char **argv ){
      int a, b;
      b = 1;
      for( a=0; a<10; a++ ){
          b *= 2;
      }
      return b;
  }
  那么这个函数所对应的中间代码是这样的:
  fun: main 2-args: argc argv b a
       @0 = b = 1
       @1 = a = 0
       JMP 7
    LAB_5:
       @4 = b *= 2
    LAB_6:
       @3 = a ++
    LAB_7:
       @2 = a < 10
       JNE 5
    LAB_8:
       @5 = b

转载于:https://www.cnblogs.com/linxr/archive/2012/01/04/2311627.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/302985.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NET问答: 对 Linq 中的 Union 和 Concat 的用法困惑

咨询区 Prasad Kanaparthi&#xff1a;我在使用 Union 和 Concat 上有一个困惑&#xff0c;从字面上理解&#xff1a;一个是并集&#xff0c;一个是连接&#xff0c;下面的例子就是我对这两个扩展方法的理解。static void Main(string[] args){var a1 (new[] { 1, 2 }).Union(…

中止是怎么用的_多士炉怎么用 使用多士炉注意事项

阅读本文前&#xff0c;请您先点击上面的蓝色字体&#xff0c;再点击“关注”&#xff0c;这样您就可以继续免费收到最新文章了。每天都有分享。完全是免费订阅&#xff0c;请放心关注。 …

国内 GitHub 造假黑色产业链曝光;开源开发者撤销对 ICE 禁用的决定

0、国内 GitHub 被爆造假&#xff0c;起底背后的黑色产业链作为全球最大的开源社区&#xff0c;GitHub 对于程序员群体而言像是空气般重要的存在&#xff0c;而互联网公司也会通过 GitHub 来进一步了解面试者的编程习惯&#xff0c;技术水平等。但最近知乎上有一篇《中国内地 G…

17款加速效率的CSS工具

作为一个网站设计/开发人员&#xff0c;你必须不断寻找方法来减少设计/开发过程中所花费的时间。这对于提高你的工作效率并最大化你的利润是非常重要的。下面介绍的按功能分类的CSS工具可以有效地节省你设计网站的时间。 表格类 1、CSS Form Code Maker – 很方便地生成“五颜六…

Azure data studio 跨平台数据库管理工具试用

最近折腾 azure sql database 的时候发现了微软的一款新的数据库管理工具&#xff1a;azure data studio。从名字上看 azure data studio 好像是专门为 azure 开发的&#xff0c;其实并不是这样的 。它同样支持对传统sql server的查询与管理。azure data studio 是一款跨平台数…

顺丰gis产品经理_线上面试季丰图科技—顺丰旗下专注GIS领域

WHITer内推——每天9点发布武汉优质互联网企业最新岗位内推机会。内推微信号&#xff1a;whxiaowai 内推邮箱&#xff1a;770554595qq.com01 Java高级开发工程师 12-25K点击查看职位详情 申请内推02高级python开发工程师 12-25K点击查看职位详情 申请内推03 物联网数据运营分析…

腾讯发布95页重磅报告:全面预测中国互联网未来5年趋势

腾讯科技企鹅智酷联合63位互联网行业领袖与专家发布了《企鹅智酷中国科技&互联网创新趋势白皮书&#xff08;2017&#xff09;》&#xff0c;预测了未来5年的中国互联网发展趋势。此次公开发布的版本共95页PPT&#xff0c;其中“移动直播产业”、“媒体与内容创业”、“互联…

ubuntu下mysql-python模块的安装

安装步骤&#xff1a; 1、sudo apt-get install python-setuptools 2、sudo apt-get install libmysqld-dev 3、sudo apt-get install libmysqlclient-dev 4、sudo apt-get install python-dev 5、sudo easy_install mysql-python 测试下&#xff1a; 在python交互式窗口&#…

.NET 6 平台系列1 .NET Framework发展历程

自1995年互联网战略日以来最雄心勃勃的事业 —— 微软.NET战略, 2000年6月30日。微软公司于2002年2月13日正式推出第一代.NET平台 .NET Framewrok 1.0。借助于自家强大易用的 Windows 系统&#xff0c;.NET Framework1.0 主要提供了面向 Windows 桌面&#xff08;Windows Form&…

3 src 获取_CVE-2019-15846:Exim远程获取root权限漏洞分析

报告编号&#xff1a;B6-2019-103101报告来源&#xff1a;360-CERT报告作者&#xff1a;360-CERT更新日期&#xff1a;2019-10-310x00 漏洞背景2019年9月6日18&#xff1a;00&#xff0c;exim发布exim-4.92.2版本修复了CVE-2019-15846&#xff0c;攻击者可以利用此漏洞远程获取…

jwt与token+redis,哪种方案更好用?

问&#xff1a;jwt与tokenredis&#xff0c;哪种方案更好用&#xff1f;其实JWT就是Json Web Token&#xff0c;就是Token的典型方式。题主的JWT和TokenRedis的区别&#xff0c;其实都是Token&#xff0c;只是JWT的可靠性保障是来源于加密算法(对称加密和非对称两种)&#xff0…

ADO.NET的记忆碎片(六)

校验DataSet中的数据数据库提供了很多的机制使数据是有效的。ADO.NET的DataSet提供了许多可在数据库系统中使用的相同的数据效验机制。一般可以将这些效验的机制分成两类&#xff1a;列级别的限制和表级别的效限制。列级别的限制&#xff1a;验证DataColumn的属性DataColumn对象…

想不到吧?数学还有如此妙用!

随着科技的快速发展&#xff0c;人工智能的重要性日渐显现。对于大多数新手来说&#xff0c;弄清楚入门人工智能需要哪些数学基础、需要熟悉什么框架等&#xff0c;都至关重要。机器学习是一个异常丰富的研究领域&#xff0c;有大量未解决的问题&#xff1a;公正、可解释性、易…

python函数列表永久修改_python 禁止函数修改列表的实现方法

有时候&#xff0c;需要禁止函数修改列表。例如要对裂变进行修改操作&#xff0c;也要保留原来的未打印的设计列表&#xff0c;以供备案。为解决这个问题&#xff0c;可向函数传递列表的副本而不是原件&#xff1b;这样函数所做的任何修改都只影响副本&#xff0c;而丝毫不影响…

ASP.NET Core 开源项目 nopCommerce,一款沉淀13年的电商开源佳作!

技术在不断更新迭代&#xff0c;.NET 6 的正式版也即将正式发布&#xff0c;在.NET Core 开源项目方面&#xff0c;CMS的代表作是SiteServer&#xff0c;商城的开源系统有没有什么代表作&#xff1f;肯定是有的&#xff0c;强烈推荐这套开源免费的商城系统&#xff1a;nopComme…

Base PyQt4, Simple Web APP Framwork

长时间以来&#xff0c;一直针对Linux 服务器开发后台程序&#xff0c;每天面对的是黑框框&#xff0c;输出只有日志文件。偶尔需要模拟客户端测试&#xff0c;要么是写几行php代码&#xff0c;在浏览器上点一点&#xff0c;要么是写个小Python脚本在shell中执行一下。写了一些…

机器学习核心算法之——贝叶斯方法

1.贝叶斯公式贝叶斯公式已经成为机器学习的核心算法之一&#xff0c;诸如拼写检查、语言翻译、海难搜救、生物医药、疾病诊断、邮件过滤、文本分类、侦破案件、工业生产等诸多方面都有很广泛的应用&#xff0c;它也是很多机器学习算法的基础。在这里&#xff0c;有必要了解一下…

python的文件操作os_python文件、文件夹操作OS模块

一、python中对文件、文件夹操作时经常用到的os模块和shutil模块常用方法。1.得到当前工作目录&#xff0c;即当前Python脚本工作的目录路径: os.getcwd()2.返回指定目录下的所有文件和目录名:os.listdir()3.函数用来删除一个文件:os.remove()4.删除多个目录&#xff1a;os.rem…

B 站面试官:“啥是重定向?”

三分钟&#xff0c;带你学习和实践域名重定向大家好&#xff0c;我是鱼皮&#xff0c;今天分享 重定向 小知识&#xff0c;以及我在腾讯云云开发中实现域名重定向的实践。孽起之前&#xff0c;我开发了一个编程导航网站&#xff0c;将网站放到了腾讯云云开发上&#xff0c;用云…

java多线程 sleep()和wait()的区别

接触了一些多线程的东西&#xff0c;还是从java入手吧。 相信看这篇文章的朋友都已经知道进程和线程的区别&#xff0c;也都知道了为什么要使用多线程了。 这两个方法主要来源是&#xff0c;sleep用于线程控制&#xff0c;而wait用于线程间的通信&#xff0c;与wait配套的方法还…