lex编译dos命令_Lex和Yacc入门教程(八).使用堆栈编译语法

本文介绍了如何使用堆栈编译语法,通过一个完整的示例展示了如何利用Lex和Yacc创建一个编译器,支持整型、浮点型、字符串型的运算和控制结构。示例代码包括对变量存储、运算规则、控制结构的处理,以及错误处理和注释支持。
摘要由CSDN通过智能技术生成

Lex和Yacc应用方法(八).使用堆栈编译语法

草木瓜  20070604

一、序

前面一些系列文章着重介绍了递归语法树在编译理论方面的应用。本文则会介绍另一种

实现方式----堆栈。

堆栈在底层系统有十分广泛的应用,同样也十分擅长处理语法结构,这里通过实际示例

探讨如何构造堆栈完成语法分析。

重要补充:下面是本系列文章全示例代码统一的调试测试环境,另对于lex,yacc文件需

要存储为Unix格式,这一点和Linux,Unix下shell很类似,DOS格式的Shell是不能够被执行

的,同样bison,lex编译DOS格式文件会出错误提示:

Red Hat Linux release 9 (Shrike)

Linux 2.4.20-8

gcc version 3.2.2 20030222

bison (GNU Bison) 1.35

lex version 2.5.4

flex version 2.5.4

二、具体示例

本示例主要完成功能:

1  支持整型,浮点型和字符串型

2  支持变量存储,变量名可为多个字符

3  支持整型,浮点型的+-*/()=运算法则

4  支持字符串型赋值

5  支持print打印整型,浮点型和字符串型

6  支持打印变量值

7  支持while if else switch四种控制结构,并支持控制结构的嵌套

8  支持> >= < <= != == 六种比较运算,同时也支持字符串的比较

9  支持 && || 复合比较运算

10 支持对空格和TAB的忽略处理

11 支持#的单行注释

12 支持{}多重组合

13 支持编译错误的具体显示

14 支持外部变量值传入(整型,浮点型和字符型)

15 支持外部变量获取(整型,浮点型和字符型)

16 完整的企业应用模式

三、示例全代码(略)

A.stack.l

----------------------------------------------

B.stack.y

----------------------------------------------

C.stack.h

----------------------------------------------

D.stackparser.c

----------------------------------------------

E.public.h

----------------------------------------------

F.main.c

----------------------------------------------

G.mk 编译shell文件

----------------------------------------------

bison -d stack.y

lex stack.l

gcc -g -c lex.yy.c stack.tab.c stackparser.c

ar -rl stack.a *.o

gcc -g -o lw main.c stack.a

H.mkclean shell文件

----------------------------------------------

rm stack.tab.c

rm stack.tab.h

rm lex.yy.c

rm *.o

rm *.a

rm lw

四、思路说明

上面列出的代码是目前最长的。

可见写一个堆栈编译器并不是一簇而就的事情,即使对于目前的实例,也需要有很多完

善的地方。设计一个堆栈编译器,我们往往需要从最简单最容易的语句开始。

A.简单的堆栈分析思想

我们先举一个简单的例子,a=1+2。要完成这个公式的计算,我们首先需要将1和2,压

入堆栈,然后分析到+运算,此时又需要将1和2出栈,执行+法后将3压入。继续分析,需要

压入a,在最后的=运算时,将3和a出栈进行赋值运算后,将a入栈。运作序列如下:

id:  0 act:  pushvalue

id:  1 act:  pushvalue

id:  2 act:        add

id:  3 act:    pushvar

id:  4 act:     assign

使用堆栈进行编译的难点在于,将无比复杂的语法结构抽象到简单的入栈出栈操作。单

从这个角度讲,是很难一步倒位的。一般地,我们需要先将指令字符串根据设定的语法归并

规则编译成有序的指令序列。然后对指令序列制定堆栈动作执行函数,依次执行指令并调用

相应函数。

值得欣慰的是,早在N年前,外国人就形成了一套十分强大的编译理论体系(lex,yacc)

去完成归并语法的工作。我们只需实现外部的规则动作。

B.lex和yacc的归并语法设计

与前面例子相似的是,使用G_Var存储编译时的变量信息,G_sBuff存储编译语句,这里

又增加了G_String统一存储编译语句中的所有字符串。至于lex和yacc设计方法也是相近的,

只是冗余了一些语句标志,如ifx,elsex,switchx等。这些标志是为了生成顺序正确的指令

序列。

lex和yacc会把编译后的所有结果指令存于G_Command中。见AddCommand。

/* 内存指令集结构 */

typedef struct {

int iTypeAction;

int iTypeVal;

float fVal;

int iVar;

int iString;

int iControl;

} TCommand;

这个指令集的设计是关键所在,iTypeAction说明这个指令的类型,iTypeVal表示指令

的值类型。fVal存储整型浮点型数值,iVar存储变量索引,iString如果不是-1,则表示字

符串的索引,iControl表示指令返回的控制信息。

C.堆栈编译

lex和yacc编译后会把指令生成到G_Command中,随后对G_Command进行遍历处理,并调

用相关动作函数进行出入栈操作。(见Act系列函数) 这里出入栈操作的是G_Command索引,处

理的结果皆存于G_Command中,这是外人比较难以理解的一点。

TCommand结构体元素是相对独立的,fVal,iString互斥,iVar标志变量索引,iControl

只用于控制堆栈的值。

在刚开始时需要对各种语法结构做统筹分析。比如拿分支和循环这类语句来说,if分支

就需要维护一个控制状态,由于嵌套语句的存在,这个控制状态需要具有堆栈的特点。每次压

入新if语句要进行现有堆栈的判断,如果前一if为false,这个if即使为true也还是false,另

外else也要做相似处理。之后对于endif做出栈操作,标志这对if/else已处理。switch比if要

稍微复杂一些,还要记录原始值,每次case要做比较。while不仅需要条件还要进行跳转,这是

Act动作函数有返回值的重要原因。

以上这个分析要进行比较体系化的考虑,这些也便于以后的功能扩展,如goto等。

这里我引入了StackValue和StackControl两个堆栈。Value用于普通的顺序计算,Control

用于if else switch while等控制结构。关于控制堆栈,可以参见Act_If,Act_Else等控制

动作函数,在编译指令序列时,会读取控制信息来判断是否执行该指令。值的注意的是,Act

动作函数还返回了下一指令的索引,这主要用于对循环,跳转等方面的处理,默认是顺序执行。

总得来说,TCommand的元素含义要独立,并严格保证处理指令时G_Command的指令数据的

合法性。

D.变量传值和获取

还是回调函数的思想,只是由于字符串的存在制造了一些小麻烦,所以使用了二级指针,

并通过返回值类型,来进行外部判断处理。不过Linux Unix下的gcc不支持引用传值,这里使

用的都是指针传递。

五、一些注意事项

A.stack.l,stack.y 文件要求为Unix格式,这一点和Linux,Unix下shell很类似,DOS

格式的Shell是不能够被执行的,同样bison,lex编译DOS格式文件会出错误提示。

B.SegmentFault多半产生于内存越界(前面已经着重说明过),除此以为还经常出现这类

情况,产生这类错误的位置的代码并无错误,但是内存值已出现乱码,这一般是指针使用不当。

C.避免一切的warning项,比如在stack.y中,将函数的预说明去除,会提示warning,但

是在执行中,函数传值就会发生根本性的错误。

D.还在在编译C/C++出现的堆栈溢出错误,当然可以用ulimit查看参数,但多半也由内存

越界有关,遇到莫名其妙的错误第一点就要想到内存问题。

E.stack.l,stack.y 注意 规则应用顺序,shift-reduce的顺序

F.耐心才是最重要的,尽量多打印一些调试信息,对于复杂的语法结构调试起来并不是很

轻松的事。

六、总结

lex和yacc应用目前是在Unix/Linux平台下,生成的是C代码,固然C++使用这些接口没有

问题,但不能满足Windows平台的使用需求。从下文起会开始介绍Windows下这类工具的使用,

以及C/C++/Java代码的生成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值