跟我一起写编译器(一)——lex&yacc【转载】

跟我一起写编译器(一)——lex&yacc【转载】

出处http://www.cnblogs.com/lucasysfeng/p/4842310.html  

 第一节、工欲善其事,必先利其器。

  笔者不会过多地陈述理论,而是希望通过实践还原一个C编译器的完成过程。

  先来看一个简单的源文件main.c:

1
2
3
4
5
6
7
#include <stdio.h>
 
int  main()
{
      printf (“Hello World\n”);
      return  0;
}

  我们会用gcc main.c生成可执行文件a.out, 那么问题来了,gcc是如何识别include int main printf return这些词汇的呢,又是如何根据这些词汇做出相应操作的呢?答案是使用lex和yacc.

  lex 代表 lexical analyzar(词法分析器),yacc 代表 yet another compiler compiler(编译器代码生成器)。lex和yacc在UNIX下分别叫flex和bison. 简单地理解下lex&yacc, lex词法分析器,读取文件中的关键词(后面说到的token标记其实可看做关键词);然后把关键词递交给yacc,yacc对一些关键词进行匹配,看它们是否符合一定的语法逻辑,如果符合就进行相应动作

  我们使用lex&yacc写编译器,所以先来学习下lex&yacc吧。

 

第二节、一个简单的lex程序。

  跟着笔者将下面的程序编译运行一遍,相信你会有所收获。

1. 程序代码。

  文件名:test.l
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 第一段 */
%{
     int  chars = 0;
     int  words = 0;
     int  lines = 0;
%}
 
/* 第二段 */ 
%%
[a-zA-Z]+  { words++; chars +=  strlen (yytext); }
\n         { chars++; lines++; }
.          { chars++; }
%%
 
/* 第三段 */ 
main( int  argc,  char  **argv)
{
     yylex();
     printf ( "%8d%8d%8d\n" , lines, words, chars);
}

分析:

  这段lex程序的作用是:根据输入的字符串,输出其行数、单词数和字符的个数。

(1) %%把文件分为3段,第一段是c和lex的全局声明,第二段是规则段,第三段是c代码。

(2) 第一段的c代码要用%{和%}括起来,第三段的c代码不用。

(3) 第二段是规则段,[a-zA-Z]+  \n   . 是正则表达式,表示匹配的内容,{}内的是c编写的动作。

  上面程序中yytext是lex变量,匹配模式的文本存储在这一变量中。yylex()这一函数开始分析,它由lex自动生成。关于lex变量和函数后续介绍,这里只是通过简单的lex程序来认识lex. 

2、按照下面过程编译运行。

#flex test.l

#gcc lex.yy.c –lfl

#./a.out

然后输入一段文字,按ctrl+d结束输入,则会输出行数,单词数和字符的个数。

编译过程和运行结果等见下图:

image

 

第三节、lex进阶。

  修改第二节程序,将正则表达式放在全局声明中,使逻辑更清晰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
%{
int  chars = 0;
int  words = 0;
int  lines = 0;
%}
 
mywords [a-zA-Z]+
mylines \n
mychars . 
 
%%
{mywords}  { words++; chars +=  strlen (yytext); }
{mylines}  { chars++; lines++; }
{mychars}  { chars++; }
%%
 
main( int  argc,  char  **argv)
{
  yylex();
   printf ( "%8d%8d%8d\n" , lines, words, chars);
}

  编译运行同第二节。

 

第四节、lex再进阶—循环扫描。

  下面给出一个lex程序,这个程序在扫描到 +  或 - 时做一个特殊输出。当调用yylex()函数时,若扫描到return对应的标记时,yylex返回,且值就为return后的值;若没扫描到return对应的标记,yylex继续执行,不返回。下次调用自动从前一次的扫描位置处开始。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
%{
enum  yytokentype
{
     ADD = 259,
     SUB = 260,
};
%}
  
myadd    "+"
mysub    "-"
myother .
  
%%
{myadd}    {  return  ADD; }
{mysub}    {  return  SUB; }
{myother}  {  printf ( "Mystery character\n" ); }
%%
  
main( int  argc,  char  **argv)
{
     int  tok;
  
     while  (tok = yylex())
     {                          
         if  (tok == ADD || tok == SUB)
         {
             printf ( "meet + or -\n" );
         }
         else
         {
             printf (" this  else  statement will not be printed, \
                    because  if  yylex  return ,the retrun value must be ADD or SUB.");
         }
     }
}

 编译同上,运行结果见下图:

image

   

  到现在,仅仅介绍了lex,后续会介绍yacc.


lexyacc构造汇编器vasm及其指令模拟器vsim vasm及vsim源于Designing Digital Computer Systems with Verilog书中定义的VeSPA(个小型的RISC指令集的CPU)的指令集。 vasm通过两遍扫描的方式将VeSPA的汇编程序翻译为机器指令。 vsim模拟CPU的取指->译码->执行的循环完成机器指令的逐条执行,直到遇到停机或者运行时错误为止。 阅读及DIY该代码,你将深入理解并学会:1.两遍扫描的汇编器的工作原理,及其汇编器的设计程序编。2.lexyacc工具在汇编器器指令模拟器的构造过程中的应用。3.CPU的指令执行过程。4.对理解计算机体系结构有参考意义。5.提供了若干.asm汇编源程序样例以进行程序测试。 /////README file ############################################################## # README file for VeSPA assembler & instruction simulation # snallieATtomDOTcom # Sat Nov 15 13:44:43 CST 2014 ############################################################## 1.to build # make or # make clean; make then vasm and vsim be made 2.run vasm # ./vasm ./asm_example/count.org.asm then ./asm_example/count.org.hx produced 3. run vsim # ./vsim ./asm_example/count.org.hx snapshot of vsim as the following: [root@rh9 vas]# ./vsim ./asm_example/count.org.hx Designing Digital Computer Systems with Verilog, 2005 David J. Lilja and Sachin S. Sapatnekar http://www.arctic.umn.edu/vespa/ VeSPA Instruction Interactive Simulator. version 0.01 snallieATtomDOTcom, Thu May 2 05:40:37 CST 2013 built: Nov 15 2014 - 13:51:25 hint: 'h' or '?' for help CPU_endian = BIG [r]>> u 0 1c @0000 50000018 | LD r0 , 0x18 @0004 58400000 | LDI r1 , #0 @0008 08430001 | ADD r1 , r1 , #0x1 @000c 38020000 | CMP r1 , r0 @0010 46fffff4 | BLE 0x8 @0014 f8000000 | HLT @0018 0000000a | NOP @001c 00000000 | NOP [w]>> w a All watch registers enabled. [w]>> t 0004: 58400000 LDI r1 , #0 @0008 08430001 ADD r1 , r1 , #0x1 PC =00000008 N=0 Z=0 C=0 V=0 r0 =0000000a r1 =00000000 r2 =00000000 r3 =00000000 r4 =00000000 r5 =00000000 r6 =00000000 r7 =00000000 r8 =00000000 r9 =00000000 r10=00000000 r11=00000000 r12=00000000 r13=00000000 r14=00000000 r15=00000000 r16=00000000 r17=00000000 r18=00000000 r19=00000000 r20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值