L语言编译器的设计与实现之设计篇

来源:互联网 发布:广联达2013软件下载 编辑:程序博客网 时间:2024/06/05 18:24

L语言编译器的设计与实现之设计篇

1、任务:实现一个简单的编译程序,能够对指定程序设计语言进行编译。

编译器组成成分

    1.扫描器

    该扫描器是一个子程序,其输入是源程序字符串,每调用一次输出一个单词符号。为了避免超前搜索,提高运行效率,简化扫描器的设计,假设程序设计语言中,基本字不能用作一般标识符,如果基本字、标识符和常数之间没有确定的运算符或界符作间隔,则用空白作间隔。

    2.语法分析器

    以算法优先分析方法为例,设计一个算符优先语法分析程序。算符优先分析属于自下而上的分析方法,该语法分析程序的输入是终结符号串(即单词符号串,以一个“#”结尾),如果输入串是句子则输出“YES”,否则输出“NO”和错误信息。当然,也可采用预测分析等方法设计语法分析器,具体方法自定。

    3.语法制导翻译程序

    采用语法制导翻译方法,实现算术表达式、赋值语句和基本控制语句等的翻译。本语法制导翻译程序的输入是终结符号串(即单词符号串,以一个“#”结尾),如果输入符号串是句子,则按照其语义进行翻译,输出等价的四元式序列。

    4.目标代码生成器

    将程序设计语言的中间代码程序翻译为目标代码程序,其输入是四元式序列,输出是一个汇编代码文件。

 

    每个环节可作为一个独立的实践课题,分别编程调试,然后再连接在一起总调,从而实现一个完整的编译器。

2、L语言定义

  程序定义:

    〈程序〉→ program〈标识符〉〈程序体〉.

    〈程序体〉→〈变量说明〉〈复合句〉

  变量定义:

    〈变量说明〉→ var〈变量定义〉|ε

    〈变量定义〉→〈标识符表〉:〈类型〉;|〈标识符表〉:〈类型〉;〈变量定义〉

    〈标识符表〉→〈标识符〉,〈标识符表〉|〈标识符〉

  语句定义:

    〈复合句〉→ begin〈语句表〉end

    〈语句表〉→〈执行句〉;〈语句表〉|〈执行句〉   (执行句:while  , If  ,赋值语句)

    〈执行句〉→〈简单句〉|〈结构句〉

    〈简单句〉→〈赋值句〉

    〈赋值句〉→〈变量〉:=〈表达式〉

    〈变量〉→〈标识符〉

    〈结构句〉→〈复合句〉|〈if句〉|〈WHILE句〉

    〈if句〉→ if〈布尔表达式〉then〈执行句〉| if〈布尔表达式〉then〈执行句〉else〈执行句〉

    〈while句〉→ while〈布尔表达式〉do〈执行句〉

  表达式定义:

    〈表达式〉→〈算术表达式〉|〈布尔表达式〉

    〈算术表达式〉→〈项〉+〈算术表达式〉|〈项〉-〈算术表达式〉|〈项〉

    〈项〉→〈项〉*〈因子〉|〈项〉/〈因子〉|〈因子〉

    〈因子〉→〈算术量〉

    〈算术量〉→〈标识符〉|〈整数〉|〈实数〉

    〈布尔表达式〉〈布尔项〉or〈布尔表达式〉|〈布尔项〉

    〈布尔项〉〈布尔因子〉and〈布尔项〉|〈布尔因子〉

    〈布尔因子〉→ not〈布尔因子〉|〈布尔量〉

    〈布尔量〉〈布尔常数〉|〈标识符〉|(〈布尔表达式〉)|〈关系表达式〉

    〈关系表达式〉〈标识符〉〈关系运算符〉〈标识符〉

    〈关系运算符〉|〈= ||〉=||〈〉

  类型定义:

    〈类型名〉→ integer|bool | real

  单词定义:

    〈标识符〉→〈字母〉|〈标识符〉〈字母〉|〈标识符〉〈数字〉

    〈整数〉→〈数字〉|〈整数〉〈数字〉

    〈实数〉→〈整数〉.|〈实数〉〈数字〉

    〈布尔常数〉→ true|false

  字符定义:

    〈字母〉→  A│B│C│D│E│F│G│H│I│J│K│L│M│N│O│P│Q│R│S│T│

    U│V│W│X│Y│Z│a│b│c│d│e│f│g│h│i│j│k│l│m│n│o│

    p│q│r│s│t│u│v│w│x│y│z

    〈数字〉→  0│1│2│3│4│5│6│7│8│9

3、源程序书写格式规定

   (1) 单词必须在一行内写完,即:一个单词不能分两行写;

   (2) 源程序语句的书写采用自由格式,即:一行可写多个语句,一个语句也可分多行写;

   (3) 源程序不含注释;

   (4) 语句以“;”结束,“end”前的一个语句的“;”不可以省略。


 第二节  词法分析

1、目的

   通过设计调试词法分析程序,实现从源程序中分出各种单词的方法;加深对课堂教学的理解;提高词法分析方法的实践能力;掌握词法分析器作为子程序以及一遍的处理过程。

            

2、任务

   (1) 能对任何L语言源程序进行分析;

   (2) 采用问答方式输入源程序文件名,然后进行词法分析;

   (3) 分割单词并转换成机内表示形式,形成token文件(单词序列)、符号表文件;

   (4) 删除空格等无用符号;

   (5) 错误处理

    给出的错误信息包括:总的出错个数,每个错误所在行号,错误编号及说明;

     l 只处理以下两种错误,其它可不必考虑

      1.非法字符:删除,即,不写入token文件

       2.错误单词

         a)包括三种形式:

         i.数字开头的数字、字母串,如:3a56

         ii.实数中出现两个小数点,如:3.14.15

         iii.实数的小数部分出现字母,如:5.26B78

          b)处理方式:截去后面出错部分,使其成为一个正确单词(即:常数)。如:3a56转换为33.14.15转换为3.145.26B78转换为5.26

3、数据结构

  3.1输入

    L源程序,为文本文件。

  3.2输出

    一个单词序列文件(即:token文件)和一个符号表文件,并输出错误信息。

   (1) token文件结构

    typedef struct token

    {

     int label;  //单词序号

     char name[30];  //单词本身

     int code;  //单词的机内码

     int addr;  //地址,单词为保留字时为-1,为标识符或常数时为大于0的数值,即在符号表中的入口地址。

    } token;

     单词的机内码表示:

      

   (2) 符号表文件结构

     符号表用来存放L语言源程序中出现的标识符和常数,文件结构如下:

            typedef struct symble

           {

             intnumber;  //序号

             inttype;  //类型

             charname[30];  //名字

           } symble;

4、词法分析程序流程图

                            

 


第三节  语法/语义分析

1、目的

   通过设计、编制、调试一个典型的语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,进一步掌握常用的语法分析方法。

2、任务

   在词法分析程序产生的token文件、符号表文件基础上,完成语法和语义分析,产生相应的中间代码——四元式序列。在此,可把语法/语义分析作为独立的一遍进行处理。

       

采用如下四元式:

操作码助记符

四元式

意  义

:=

 ( 51, a, 0, r )

r←a

+

 ( 43, a, b, r )

r←a+b

-

 ( 45, a, b, r )

r←a-b

*

 ( 41, a, b, r )

r←a*b

/

 ( 48, a, b, r )

r←b/a

j<

 ( 53, a, b, n )

  若a<b转至第n个四元式 

j<=

 ( 54, a, b, n )

  若a<=b转至第n个四元式 

j>

 ( 57, a, b, n )

若a>b转至第n个四元式

j>=

 ( 58, a, b, n )

若a>=b转至第n个四元式

j =

 ( 56, a, b, n )

若a=b转至第n个四元式

j

 ( 52, 0, 0, n )

转至第n个四元式

j <>

 ( 55, a, b, n )

若a<>b转至第n个四元式

3、数据结构

 3.1输入

   token文件、符号表文件,其数据结构与词法分析产生的文件相同。

 3.2输出

   l 四元式序列文件,其纪录结构如下:

   typedef struct equ

   {

    int op;  //四元式操作码

    int op1;  //操作数在符号表中的入口地址

    int op2;  //操作数在符号表中的入口地址

    int result;  //结果变量在符号表中的入口地址

   } equ;

   l 程序中可用数组Equ存放四元式序列,定义为:equ Equ[EQU_LEN]

   l 可用变量int LineOfEqu做指针,指向下一个即将产生的四元式

   l 符号表文件的结构与输入相同,语法分析中对于符号表不做操作,只是在文件头部增加一个记录变量多少的数据。

4、程序结构说明

 为方便编程,将语言文法整理如下:

     L→S| S ; L

     S→id:= E

     S→ifB then S

     S→ifB then S else S

     S→whileB do S

     S→beginL end

  变量说明语句的文法:

     S→varD|ε

     D→L: K ; | L : K ; D

     L→i, L | i

     K→integer| bool | real

  其中,B表示布尔表达式,E表示算术表达式

  可采用递归下降分析法或其它方法进行语法分析。语法/语义分析程序可划分为三个模块,结构如下:

    

     


 

 


第四节 目标代码生成

1、目的

   实践目标代码的生成方法。

2、任务

   编写一个目标代码生成程序,将L语言的中间代码程序翻译为目标代码程序(汇编语言程序),如下图:

         

  目标机说明:

    l  以8086微处理机为目标机,生成8086汇编指令

    l  8086是16位微处理器,数据总线为16位,地址总线为20位,可寻址1MB的空间

    l  8086有8个16位通用寄存器和一个标志寄存器。这8个寄存器AX-DI都可以用作累加器。其中,BX和BP(基地址指针)寄存器通常用于指定数据区的基址,称为基址寄存器,SI和DI大多用来表示相对基址的偏移量,称为变址寄存器

    l  8086的地址空间是分段的,每段64KB

    l  简单起见,本实验不涉及段间寻址,数据与代码都放在一个段内

    l  实验中选用以下寻址功能,圆括号表示取其内容:

        n  寄存器寻址: MOV AX,BX;

          功能:AX←(BX)

        n  直接寻址:MOV AX,DATA;

          功能:AX←(DATA)

    l  常用指令:

        n  传送指令:r表示寄存器,m表示内存单元

          MOV  r, r/m    r←(r/m), r/m表示r或m

          MOV  r/m, r    r/m←(r)

          MOV  r/m, imm r/m←imm, imm是立即数

        n  运算指令:包括ADD,SUB,MUL,DIV,CMP等。下面以ADD为例说明其用法:

          ADD  r, r/m     r←(r)+(r/m)

          ADD  r/m, r/imm r/m←(r/m)+(r)或imm

        n  CMP只影响标志位,不影响操作数的大小

        n  转移指令:Z是标志位,S是符号位,O是溢出位

 

指令码

意义

条件

JZ, JE

结果为0或相等则转

Z=1,(A) = (B)

JNZ, JNE

结果不为0或不相等则转

Z=0,(A)≠(B)

JNL, JGE

大于等于转

(S∨O)=0,(A)≥(B)

JL, JNGE

小于转

(S∨O)=1,(A)<(B)

JG, JNLE

大于转

(S∨O∨Z)=0,(A)>(B)

JMP

无条件转移

 

3、数据结构

  3.1输入

    四元式序列文件和符号表文件,其结构与语法/语义分析程序的输出一致。

  3.2输出

     一个汇编代码文件,并无特殊数据结构。

4、程序参考结构:

  将中间代码程序(四元式序列)翻译成汇编程序可按以下步骤进行:

    (1) 划分基本块

    (2) 对每个基本块生成基本块的目标代码

      

    为了划分和记录基本块,对四元式结构作以下修改:

      typedef struct GenStruct

      {

        int label;

        char op[4];

        int code;

        int addr1;

        int addr2;

        int result;

        int out_port;  //记录该四元式是否为一个基本块的入口,是则为1,否则为0。

      } GenStruct;

5、寄存器分配策略

  主要采用四个通用寄存器:ax, bx, cx, dx, 其中,ax, cx的作用固定,ax用作累加器,cx用作循环计数器,在四元式翻译时直接应用不再分配。所以分配策略只用于bx与dx,具体算法如下:

    if (bx未被使用或已分配给了变量a) {

      bx分配给变量a;

    }

    else {

      if (dx未被使用或已分配给了变量a){

        dx分配给变量a;

      }

      else {

        其它策略;

      }

    }

   注:a为变量在符号表的入口地址。

6、代码生成器的模块结构及说明

                               

   实现时,整个程序的四元式表和目标代码文件说明为全局数据。调用划分基本块模块之后,返回新的四元式序列(带入口标记)。先根据基本块的入口,再查找下一入口,两个入口之间就是该基本块。



下一篇:
L语言编译器的设计与实现之实践篇(一)

0 0