单位文秘网 2022-02-16 08:08:51 点击: 次
摘 要:基于IEC61131-3标准的结构化文本语言(简称ST语言)是可编程控制器(简称PLC)编程语言中的一种高级文本语言,文中论述了如何结合词法分析工具Lex及语法分析工具Yacc来进行ST语言编译器前端的设计与实现,其中主要介绍了词法分析、语法分析、语义分析及中间代码生成这几个阶段所涉及到的相关数据结构与算法。
关键词:IEC61131-3标准;结构化文本语言;可编程控制器;Lex;Yacc
基于IEC61131-3标准的结构化文本语言(简称ST语言)作为可编程控制器(简称PLC)编程语言中的一种高级文本语言,其在IEC61131-3标准中的位置如图1中蓝色标记所示。它可以像其它高级语言一样用来描述功能、功能块和程序的行为,还可在顺序功能流程图(简称SFC)中描述步、动作和转变的行为。
结构化文本语言在形式上和BASIC、PASCAL或C语言很相似,它作为一种专门为工业控制应用开发的编程语言,具有很强的编程能力,编程格式自由,程序紧凑具有很强的数据处理能力,对于熟悉计算机高级语言开发的人员来说更容易理解和使用,因此其非常适合运用在具有复杂计算的应用中,可移植性好。
结构化文本语言由一系列具有逻辑结构的表达式语句构成,表达式由操作数和操作符组成,其基本的语句有赋值语句、功能块调用语句、IF结构语句、CASE结构语句、FOR结构语句、WHILE结构语句、REPEAT结构语句、RETURN结构语句、EXIT结构语句、空语句等。一个典型的求和运算的FOR语句程序如下所示:
使用结构化文本语言编写的控制程序变成PLC系统的CPU可以识别的目标语言,必须经过编译或者交叉编译程序才能完成,其所对应的PLC编程系统的结构如图2所示。本文所研究的是如何设计与实现一个可跨平台的PLC编译系统的前端程序,将结构化文本语言编写的控制程序编译成一种中间代码结构,该中间代码可以根据不同PLC系统的CPU生成其对应的汇编代码或机器语言。
1 系统分析与设计
一个完整的结构化文本语言编译系统主要包括词法分析、语法分析、语义分析及中间代码生成、目标代码生成、目标代码优化等几个主要阶段,其各个阶段的关系如图3所示。本系统的主要目标是将结构化文本语言编写的控制程序经过词法分析、语法分析及语义分析、中间代码生成这几个阶段将其转换成一种自定义的中间代码结构,而该中间代码可以根据不同的运行环境所对应的CPU指令生成目标代码。
1.1 词法分析
词法分析阶段就是将结构化文本语言编写的控制程序源程序识别成若干个单词符号,并将这些识别出来的单词符号连同其对应的相关属性一起存储到符号表中,以作为语法分析阶段的输入。一般地,词法分析阶段的第一步是将输入的源程序放在一个输入缓冲区中,进行预处理,将源程序中的注释、空格、制表符、回车符及换行符删除,而本编译系统则将预处理阶段和单词符号的识别阶段合并在一起进行处理。
(1)词法分析工具LEX简介
本编译系统的词法分析程序将利用词法分析工具LEX自动生成。LEX是Lexcial Compiler的缩写,是UNIX环境下非常著名的工具,主要功能是生成一个词法分析器(Scanner)的C源代码。LEX是一个将包含了正则表达式的文本文件作为其输入的程序,此外还包括每一个表达式被匹配时所采取的动作。描述词法分析器信息的描述文件*.L,经过LEX编译后,生成一个lex.yy.c的C源代码文件,然后由C编译器将其编译生成一个词法分析器,其执行过程如图4所示。
(2)符号表的设计
在词法分析阶段识别出来的单词符号及其对应的相关属性必须存储到一个称为符号表的存储结构中,以作为语法分析、语义分析等后续阶段的输入来源,同时在后续各个阶段也必须对其进行修改或交换信息,因此设计一个结构合理、性能稳定、高效率的符号表在整个编译过程中起到非常重要的作用。
在结构化文本语言中可将需要识别的单词符号分为关键字、标识符、常量、操作符及其它单个字符等五大类,本系统将符号表设计成一个单向链表的形式,其中单向链表中每一个节点的数据结构如下所示:
typedef struct tagSymbolNode
{
char chContent[128];//单词符号内容
int nTokenType; //单词符号的类型
int nSubType;//单词符号的子类型
int nLine;//在源程序中的行号
}SymbolNode;
因此单向链表的数据结构可设计成如下所示:
typedef struct tagSymbolList
{
SymbolNode symbolNode;//符号节点信息
tagSymbolList *pNext; //指向下一个节点
}SymbolList;
在上述所设计的符号节点结构中,单词符号的类型nTokenType则标识该单词符号属于关键字、标识符、常量、操作符等类中的哪一类。而当单词符号的类型nTokenType属于常量时,则用单词符号的子类型nSubType标识其具体属于哪一种类型的常量,如整型类型、实数类型、时间类型等。
1.2 语法分析
语法分析阶段是以经过词法分析程序所解析出来的单词符号串作为输入或分析对象,其主要任务是:根据结构化文本语言的语法规则,分析源程序的语法结构,即分析如何由这些单词符号组成各种语法范畴(如下标变量、各种表达式、各种语句、程序段或分程序等),并在分析的过程中,对源程序进行语法检查,一旦发现源程序中含有不符合语法规则的表达式或语句,则输出错误信息,提示用户进行修改。一般地,语法分析程序的输出形式有多种,通常以某种方法表示的语法树形式作为输出。
根据语法树产生方向的不同,语法分析主要分为自顶向下和自底向上两种方法。本编译系统利用语法分析工具YACC、使用自底向上方法中的LALR(1)分析法来产生语法分析的状态转换表,然后根据其状态转换表中的各种状态转换方向来进行语法检查。
(1)语法分析工具YACC简介
YACC是yet another compiler-compiler的缩写,也称之为分析程序生成器(parser generator),它主要和词法分析工具LEX一起使用生成相应的C代码,经C编译器编译后生成语法分析程序。YACC采用LALR(1)分析算法来进行语言的语法分析,它以一个包含巴克斯范式(简称BNF)表达的语法规则(也即语法产生式)和与该语法规则相匹配的动作的语法描述文件(其后缀名为*.Y)作为输入,并为该语言产生分析过程以作为它的输出的程序。YACC根据其编译时选项命令的不同而产生多种不同的输出文件,其主要有对应语法规则的语法分析程序y.tab.c和包含状态转换信息的y.output两种文件(或类似名称的文件)。
本编译系统主要利用y.output文件中的由结构化文本语言语法规则所产生的状态转换信息来指示状态的转换方向,即根据当前的状态信息来决定下一步是进行“移进”还是“规约”动作。YACC输入的结构化文本语言的部分语法规则及产生的部分状态转换信息如下所示:
① 部分语法规则
statement_start -> statement_list
statement_list -> statement_list statement
statement_list -> statement
statement -> ";"
② 部分状态转换信息
IDENTIFIER shift,and go to state 1
DIRECTVAR shift,and go to state 2
IF shift,and go to state 3
statement_start go to state 169
statement_list go to state 11
statementgo to state 12
assignment_statement go to state 13
(2)语法分析中的主要数据结构
由于本系统是利用语法分析工具YACC所产生的状态转换信息来进行语法分析及语法检查的,而YACC所使用的LALR(1)分析算法必须要有一个状态栈和一个符号栈来实现,因此在本系统也需要设计一个状态栈StateStack及一个符号栈SymbolStack。其中状态栈StateStack存储下一步将要转换的状态,符号栈SymbolStack用来存储状态转换过程中所涉及到的结构化文本语言语法规则的非终结符及终结符。
1.3 语义分析及中间代码生成
控制源程序经过语法分析及检查后已经符合结构化文本语言的语法规则,但还必须对其上下文有关性质、类型等方面进行检查,即进行语义分析,例如数组的下标必须为整型,赋值语句的左右两边类型必须匹配等语言规范要求。语义分析主要包括程序编译时进行语义检查的静态语义分析和目标程序运行时进行语义检查的动态语义分析,而本节所述的主要指静态语义分析,如标识符未声明,对于除零溢出等动态语义错误则主要利用保护措施进行处理。
在语义分析的过程中如果发现不符合语言规范要求时则输出错误信息,提示用户进行修改。控制源程序经过语法及语义分析后,如果没有错误则将其转换成一种自定义的中间代码结构。
(1)语义分析中的两个栈
在语义分析的过程中由于需要与语法分析并列进行,且如果出现语义错误则需要向用户输出错误提示信息,所以必须设计一个语义信息栈结构SemanticStack用来存储语义信息,其中栈中的数据结构可以设计成如下所示:
typedef struct tagSemanticInfo
{
string strIdentifier; //标识符名称
DWORD dwBaseType; //基本数据类型
string strComplexType;//复杂数据类型
unsigned int uiDimension;//数组维数
bool bConstant; //标识常量
bool bWrite; //标识可写属性
}SemanticInfo;
另外在语义分析结束后需要将源控制程序转换成一种中间代码结构,因此还需要一个存储中间代码信息的语法树栈结构SyntaxTreeStack,其中栈中的数据结构见下一小节所示。
(2)中间代码生成及中间代码结构
经过语法及语义分析后,需要将源控制程序转换成一种中间代码结构,该中间代码可以根据不同的CPU指令生成对应的汇编代码或机器语言。本系统的中间代码设计成一种抽象语法树形式,其中每个树节点的结构如下所示:
struct TreeNode
{
int Op; //操作类型
Type NodeType; //节点类型
TreeNode Kids[2]; //子节点
union
{
Value Cnst; //常量值
Symbol Sym; //符号
}LeafNode;
};
每个节点的形式如图5所示,均代表一种基本运算,其ASGN表示当前操作类型赋值,I表示节点类型为整型。
例如赋值语句a := b + 0.5 经过转换后所对应的抽象语法树如图6所示,其中a,b均为整型。具体运算过程如下:首先节点(ADDRG+P)取得变量b的地址,节点(INDIR+I)从变量b的地址(&b)中取得b的值,由于常量0.5默认为双精度型,所以节点(CVD+I)将变量b中的值由整型转换成双精度型;节点(CSNT+D)取得常量0.5,节点(ADD+D)进行加法运算,其结果为双精度型,由于赋值语句的左值a为整型类型,所以在赋值运算之前将结果转换为整型;左边节点(ADDRG+P)取得变量a的地址,最后将b+0.5的结果赋值给变量a。
2 总结与展望
2.1 总结
针对基于IEC61131-3标准的结构化文本语言在可编程控制器中的重要应用,本文论述了如何运用词法分析工具LEX及语法分析工具YACC来设计与实现其编译前端的词法分析与语法分析程序,并介绍了整个编译前端所用到的主要数据结构、算法及一种可针对不同PLC运行环境的中间代码结构。应用该设计方案实现的结构化文本语言编译前端程序,可根据不同的CPU指令编写相应的编译后端即可方便地运用到各种运行环境中,实现跨平台性。
2.2 展望
尽管应用本设计方案可以实现一种具有跨平台性的结构化文本语言编译前端程序,但该方案仍然有许多需要优化的地方,比如符号表的优化关系到整个编译程序的质量和效率,而本文并没有论述这一主题。同时本文也并没有论述如何将中间代码结构转换成可以实际运行的机器语言代码及相关的地址分配方案,这些工作需要在后续阶段继续完成,以实现成一个完整的编译程序。
参考文献
[1]王文义. 可编程控制器(PLC)原理与应用[M]. 北京: 科学出版社,2010.
[2]吴丽. 可编程控制器基础与应用[M] 北京: 机械工业出版社,2009.
[3]陈火旺,刘春林等. 程序设计语言编译原理[M]. 第3版. 北京:国防工业出版社,2004.
[4]Alfred V. Aho,Monica S. Lam,Ravi Sethi,Jeffrey D. Ullman著,赵建华等译. 编译原理[M]. 第2版. 北京: 机械工业出版社,2009.
[5]Jobn R. Levine,Tony Mason,Doug Brown著,杨作梅等译. lex与yacc[M]. 第二版. 北京: 机械工业出版社,2003.
作者简介
蓝海滨(1984-),男,江西省宜春市人,硕士研究生,主要研究方向为自动化控制技术。
(责任编辑:单位文秘网) )地址:https://www.kgf8887.com/show-162-99135-1.html
版权声明:
本站由单位文秘网原创策划制作,欢迎订阅或转载,但请注明出处。违者必究。单位文秘网独家运营 版权所有 未经许可不得转载使用