王亚男,陈婵娟
(陕西科技大学 机电工程学院,西安 710021)
软PLC编译模块是实现将按照指令表语言规则编写的源程序转换成等价的能在运行系统上执行的目标代码的功能。编译模块是一个高度复杂的程序,其内部结构和组织方式具有多种形式。编译模块在工作过程中,往往完成如下的任务:1)读取源代码并且获得程序的结构描述;2)分析程序结构,并且生成相应的目标代码。
人工编写编译模块的分析程序是一件非常耗时的工作,且由于人为因素,程序不如自动生成程序稳定,完善。为了简化开发过程,开发了Flex和Bison程序来解决第一个任务[1],Flex将源代码文件分解为各种词汇,Bison找到这些词汇的组成方式。根据软PLC的指令语言,生成能够解决问题的C/C++语言代码,并通过代码转换生成最终的目标代码。
编译模块的构造包括词法分析、语法分析、语义分析、错误的检查和处理以及代码生成和代码优化等程序[2]。编译过程可由一遍、两遍或多遍完成。所谓“遍”,是对源程序或其等价的中间语言程序从头到尾扫描并完成规定任务的过程。每一遍扫描可以完成一个阶段或多个阶段的工作[3]。通常,一个多遍的编译程序比一遍的编译程序占内存少,且整个编译程序的逻辑结构清晰。本系统的编译过程包含三遍,分别进行:
词法分析阶段是编译过程的第一个阶段,是编译的基础。这个阶段的任务是从左到右一个字符一个字符地读入源程序,即对构成源程序的字符流进行扫描然后根据构词规则识别单词(也称单词符号或符号)。
语法分析是编译过程的一个逻辑阶段。语法分析的任务是在词法分析的基础上将单词序列组合成各类语法短语,如“程序”,“语句”,“表达式”等等。语法分析程序判断源程序在结构上是否正确,源程序的结构由上下文无关文法描述。
将PLC指令转换为目标代码。
Flex程序分为三个段:第一段是C和Flex的全局声明;第二段包括规则( C代码);第三段是补充的C函数。例如,第三段中一般都有main()函数。这些段以%%来分界。Flex源程序结构是:<定义段>,<规则段>和<子程序段>。<定义段>的内容主要包括C语言说明:括在%{和%}之间的内容为C语言说明,所有C语言的说明语句和预处理语句都可以放在这一部分中,Flex将把%{和%}之间的内容不加改变地抄写到它所生成的词法分析程序之中去。标识符定义:Flex用标识符给某些重复出现次数较多的正则表达式命名,而在需要使用它们的地方代之以相应的标识符。开始状态说明:在Flex中提供了一种称为开始状态的机制,以解决左文相关问题。<规则段>中每一条规则都可分为正则表达式部分和C程序或动作部分。当输入流中出现与正则表达式相匹配的字符串时,就执行后面的动作。为了便于描述词法分析程序的动作,Flex 提供了许多变量、子程序和宏替换供用户使用,详细可以参见用户手册[5]。在<子程序段>,可以定义词法分析程序所需的各类过程和函数 ,比如主程序 main()或函数yywrap()。子程序段里的内容由Flex原封不动复制到它所生成的词法分析程序里面。
Bison源程序结构也由<说明段>、<规则段>和<子程序段>三部分组成。<规则段>由一条或多条规则所组成,不包含任何规则的 <规则段>是不合法的。每条规则以分号结尾。在逻辑上一条规则可分为两部分,一部分是一条巴科斯范式(BNF) ,另一部分是一段C程序,称为一个动作。当从词法分析程序得来的单词序列可按某个BNF进行归约时,就可执行与该 BNF相对应的动作。<说明段>中的说明有两类:一类是C程序说明;另一类是Bison说明。C程序说明必须用配对的%{和%}括在一起,它们将原封不动地复制到Bison所生成的语法分析程序之中去。Bison说明有多种,关键字%token的作用是说明终结符,没有在%token语句中出现的标识符都被认为是非终结符,每个非终结符必须至少在产生式的左部出现一次。在所有非终结符中,描述最一般结构的产生式左部非终结符,被称为开始符。%right、%left和%nonassoc的作用是说明算符的结合性,以解决语法的二义性问题。在<子程序段> 里,用户可以自己所需的各种子程序,比如说错误处理程序yyerror ()子程序中的内容都会被Bison如实地复制到Bison所生成的语法分析程序之中去。
词法分析是程序解释的第一步,是编译的基础。词法分析是从左到右逐个字符地对源程序进行扫描,产生一个个单词符号,把它作为字符串的源程序改造成为单词符号串的中间程序,用于随后的语法分析。本编译模块设计的词法分析程序需要完成以下任务:
1)建立PLC指令集。PlC基本指令集基本逻辑指令包括ALD、 OLD、 LPS、 LRD、 LPP、 LDS 、LD、 A 、O 、END等。
2)给出对应于上述程序字集的Flex正规式表达式,并且对应于每条词法规则,编写其被识别时应执行的动作。其中部分Flex源程序如下:
3)通过flex Flex_plc.l指令,将Flex源程序转换成c语言的词法分析程序yylex()。
在语法分析中,主要工作也是编写Bison源文件,这里将其命名为Bison_plc.y。
其说明部分要依据软PLC后续环节和运行系统的要求包含一些头文件和函数或数据的定义,例如定义目标代码存贮区、函数指针数组等。指令表的Bison_plc.y文件的说明部分如下:
在编译PLC语句表时最重要的是如何完成Bison程序中的规则段,其规则部分按照指令表语言的要求和巴克斯文法规则BNF进行编写。当我们为一种语言制定了形式语法后,就完全定义了这个语言,哪些东西是该语言的,哪些东西不是该语言的都不会有歧义了。因此当我们用定义的语法规则定义PLC的语句表语言后,经编译成功后,就可以生成PLC语句表指令的解析程序,完成对语句表的编译,当输入的语言不符合PLC指令格式时,编译程序就会报错,当语句表正确时,就会被正确编译,生成目标代码。设计中根据指令格式将语句表定义了五种匹配模式,这五种模式也就是PLC语句表的叶,根据这个原理,将每种模式继续向下分,直到终止符为止,而终止符也就是我们在词法分析器中定义的关键词,这也就是词法分析器能够识别语句表的关键,在这部分设计时最主要的是语法树的构建。
图1 布尔型操作数指令的语法树
语法分析器在工作时是至下向上工作的,因为语句表中的字母也就是在语法分析器中定义的终止符,当词法分析器将识别的单词传递给语法分析时,语法分析器就会向上匹配,执行相应的数据存储,直到遇到最终的非终止符为止,从语法树来看就是从叶寻根的方式来工作的,当完全符合末一种规则时,就会完成译码,将PLC指令中对应的地址信息存储到定义的目标代码缓冲区中,完成一条语句表的译码。以下是布尔型操作数指令对应的语法树,如图1所示。
生成的是满足C语言格式bison_plc.tab.h和bison_plc.tab.c文件。最后,将Flex_plc.c、bison_plc.tab.h和bison_plc.tab.c文件一同放在工程目录下,供开发系统中的编译菜单的响应函数OnCompile( )调用,实现指令表的编译功能。
根据上述方法,可以生成独立运行的词法分析器和语法分析器,也可以将二者结合起来,由词法分析器识别单词,传递给语法分析器处理。本文采用语法分析器调用词法分析程序,即将它们结合的方法,在一个工程中完成词法分析和语法分析模块。
Bison所生成的语法分析程序yyparse()调用的词法分析程序与flex所生成的程序都是 yylex()函数。对于Flex生成的词法分析器,和Bison结合使用时,每当yylex()读取并匹配了一个模式时,就返回一个标记,语法分析程序获得返回的标记后,进行语法分析。当Bison运行一个带有标记的.y文件时,会生成一个头文件,它对每个标记都有#define的定义,这个头文件必须在相应的Flex源文件中的C声明段中包含。
Flex与Bison结合生成编译器的步骤为[6]:
1)根据指令表的编写格式和规则,编写一个名为Flex_plc.l的语句表的Flex源文件,以及一个Bison_plc.y的Bison源文件(文件名称可以自定义,但文件类型必须为.l和.y)。
2)用Bison运行Bison_plc.y文件,生成Bison_plc.tab.c和Bison_plc.tab.h文件。因为在Flex源文件中需要包含Bison_plc.tab.h头文件,利用里面的宏定义,因此要先运行Bison_plc.y文件。
执行命令为:..bison-d Bison_plc.y
3)运行Flex_plc.l文件,生产Flex_plc.yy.c的c语言文件。执行命令为:..flex Flex_plc.l
4)用Visual C++将Bison_plc.tab.c、.Bison_plc.tab.h和 Flex_plc.yy.c连接起来编译,生成可执行文件Flex_plc.yy.exe。
5)运行Flex_plc.yy.exe,即可对指令表语言进行词法和语法分析。
图2 Flex与Bison的结合
代码转换在词法分析、语法分析完成后,如没有错误 ,语法分析程序输出目标代码。软PLC开发系统的目标代码是PLC指令的二进制编码[7],例如:操作符AND的内部码为00FF00,元件X的编码0X01,将操作符的编码与元件编码相加作为目标代码的高十六位,低十六位为元件的编号,指令AND X1的编码为00FF010001。代码转换的过程在词法分析时完成。
应用Flex和Bison编写软PLC的编译模块,它的优点是与用户自己手动编写词法与语法分析程序相比,可以减少大量的编码,并且速度和准确度都有很大提高。如果目前的指令系统发生变化,规则需要扩充或修改时,对于Flex和Bison程序来说只需修改很少的部分即可,这种易于维护和实现的方案不仅降低了软PLC编译器的开发难度也提高了效率。
[1] 基于MSYS的 Flex & Bison(编译器开发工具)使用教程.http://www.360doc.com/content/10/0424/13/1156733_246 48165.shtml.
[2] 田文琦,于东,高伟,纪元.flex和bison在软PLC编译器中的应用[J].微计算机信息,2009,7-1:235-237.
[3] 陈火旺,钱家哗,孙永强.编译原理[M].国防工业出版社,1984.
[4] Levine,John R.,Tony Mason and Doug Brown,Iex&Yacc.OReilly& Associates,Inc.Sebastopol,California,1992.
[5] GNU flex and bison,http://www.gnu.org.
[6] 熊伟,戴果.在Visual C++集成开发环境下应用flex和bison开发编译器[J].战术导弹控制技术.2004,(4):64-67.
[7] 张少坤,游有鹏.基于Lex& Yacc的PLC指令编译器的研究与实现[J].PLC&FA,34-35.