王亞男,陳嬋娟
(陜西科技大學 機電工程學院,西安 710021)
軟PLC編譯模塊是實現(xiàn)將按照指令表語言規(guī)則編寫的源程序轉換成等價的能在運行系統(tǒng)上執(zhí)行的目標代碼的功能。編譯模塊是一個高度復雜的程序,其內部結構和組織方式具有多種形式。編譯模塊在工作過程中,往往完成如下的任務:1)讀取源代碼并且獲得程序的結構描述;2)分析程序結構,并且生成相應的目標代碼。
人工編寫編譯模塊的分析程序是一件非常耗時的工作,且由于人為因素,程序不如自動生成程序穩(wěn)定,完善。為了簡化開發(fā)過程,開發(fā)了Flex和Bison程序來解決第一個任務[1],F(xiàn)lex將源代碼文件分解為各種詞匯,Bison找到這些詞匯的組成方式。根據(jù)軟PLC的指令語言,生成能夠解決問題的C/C++語言代碼,并通過代碼轉換生成最終的目標代碼。
編譯模塊的構造包括詞法分析、語法分析、語義分析、錯誤的檢查和處理以及代碼生成和代碼優(yōu)化等程序[2]。編譯過程可由一遍、兩遍或多遍完成。所謂“遍”,是對源程序或其等價的中間語言程序從頭到尾掃描并完成規(guī)定任務的過程。每一遍掃描可以完成一個階段或多個階段的工作[3]。通常,一個多遍的編譯程序比一遍的編譯程序占內存少,且整個編譯程序的邏輯結構清晰。本系統(tǒng)的編譯過程包含三遍,分別進行:
詞法分析階段是編譯過程的第一個階段,是編譯的基礎。這個階段的任務是從左到右一個字符一個字符地讀入源程序,即對構成源程序的字符流進行掃描然后根據(jù)構詞規(guī)則識別單詞(也稱單詞符號或符號)。
語法分析是編譯過程的一個邏輯階段。語法分析的任務是在詞法分析的基礎上將單詞序列組合成各類語法短語,如“程序”,“語句”,“表達式”等等。語法分析程序判斷源程序在結構上是否正確,源程序的結構由上下文無關文法描述。
將PLC指令轉換為目標代碼。
Flex程序分為三個段:第一段是C和Flex的全局聲明;第二段包括規(guī)則( C代碼);第三段是補充的C函數(shù)。例如,第三段中一般都有main()函數(shù)。這些段以%%來分界。Flex源程序結構是:<定義段>,<規(guī)則段>和<子程序段>。<定義段>的內容主要包括C語言說明:括在%{和%}之間的內容為C語言說明,所有C語言的說明語句和預處理語句都可以放在這一部分中,F(xiàn)lex將把%{和%}之間的內容不加改變地抄寫到它所生成的詞法分析程序之中去。標識符定義:Flex用標識符給某些重復出現(xiàn)次數(shù)較多的正則表達式命名,而在需要使用它們的地方代之以相應的標識符。開始狀態(tài)說明:在Flex中提供了一種稱為開始狀態(tài)的機制,以解決左文相關問題。<規(guī)則段>中每一條規(guī)則都可分為正則表達式部分和C程序或動作部分。當輸入流中出現(xiàn)與正則表達式相匹配的字符串時,就執(zhí)行后面的動作。為了便于描述詞法分析程序的動作,F(xiàn)lex 提供了許多變量、子程序和宏替換供用戶使用,詳細可以參見用戶手冊[5]。在<子程序段>,可以定義詞法分析程序所需的各類過程和函數(shù) ,比如主程序 main()或函數(shù)yywrap()。子程序段里的內容由Flex原封不動復制到它所生成的詞法分析程序里面。
Bison源程序結構也由<說明段>、<規(guī)則段>和<子程序段>三部分組成。<規(guī)則段>由一條或多條規(guī)則所組成,不包含任何規(guī)則的 <規(guī)則段>是不合法的。每條規(guī)則以分號結尾。在邏輯上一條規(guī)則可分為兩部分,一部分是一條巴科斯范式(BNF) ,另一部分是一段C程序,稱為一個動作。當從詞法分析程序得來的單詞序列可按某個BNF進行歸約時,就可執(zhí)行與該 BNF相對應的動作。<說明段>中的說明有兩類:一類是C程序說明;另一類是Bison說明。C程序說明必須用配對的%{和%}括在一起,它們將原封不動地復制到Bison所生成的語法分析程序之中去。Bison說明有多種,關鍵字%token的作用是說明終結符,沒有在%token語句中出現(xiàn)的標識符都被認為是非終結符,每個非終結符必須至少在產生式的左部出現(xiàn)一次。在所有非終結符中,描述最一般結構的產生式左部非終結符,被稱為開始符。%right、%left和%nonassoc的作用是說明算符的結合性,以解決語法的二義性問題。在<子程序段> 里,用戶可以自己所需的各種子程序,比如說錯誤處理程序yyerror ()子程序中的內容都會被Bison如實地復制到Bison所生成的語法分析程序之中去。
詞法分析是程序解釋的第一步,是編譯的基礎。詞法分析是從左到右逐個字符地對源程序進行掃描,產生一個個單詞符號,把它作為字符串的源程序改造成為單詞符號串的中間程序,用于隨后的語法分析。本編譯模塊設計的詞法分析程序需要完成以下任務:
1)建立PLC指令集。PlC基本指令集基本邏輯指令包括ALD、 OLD、 LPS、 LRD、 LPP、 LDS 、LD、 A 、O 、END等。
2)給出對應于上述程序字集的Flex正規(guī)式表達式,并且對應于每條詞法規(guī)則,編寫其被識別時應執(zhí)行的動作。其中部分Flex源程序如下:
3)通過flex Flex_plc.l指令,將Flex源程序轉換成c語言的詞法分析程序yylex()。
在語法分析中,主要工作也是編寫B(tài)ison源文件,這里將其命名為Bison_plc.y。
其說明部分要依據(jù)軟PLC后續(xù)環(huán)節(jié)和運行系統(tǒng)的要求包含一些頭文件和函數(shù)或數(shù)據(jù)的定義,例如定義目標代碼存貯區(qū)、函數(shù)指針數(shù)組等。指令表的Bison_plc.y文件的說明部分如下:
在編譯PLC語句表時最重要的是如何完成Bison程序中的規(guī)則段,其規(guī)則部分按照指令表語言的要求和巴克斯文法規(guī)則BNF進行編寫。當我們?yōu)橐环N語言制定了形式語法后,就完全定義了這個語言,哪些東西是該語言的,哪些東西不是該語言的都不會有歧義了。因此當我們用定義的語法規(guī)則定義PLC的語句表語言后,經編譯成功后,就可以生成PLC語句表指令的解析程序,完成對語句表的編譯,當輸入的語言不符合PLC指令格式時,編譯程序就會報錯,當語句表正確時,就會被正確編譯,生成目標代碼。設計中根據(jù)指令格式將語句表定義了五種匹配模式,這五種模式也就是PLC語句表的葉,根據(jù)這個原理,將每種模式繼續(xù)向下分,直到終止符為止,而終止符也就是我們在詞法分析器中定義的關鍵詞,這也就是詞法分析器能夠識別語句表的關鍵,在這部分設計時最主要的是語法樹的構建。
圖1 布爾型操作數(shù)指令的語法樹
語法分析器在工作時是至下向上工作的,因為語句表中的字母也就是在語法分析器中定義的終止符,當詞法分析器將識別的單詞傳遞給語法分析時,語法分析器就會向上匹配,執(zhí)行相應的數(shù)據(jù)存儲,直到遇到最終的非終止符為止,從語法樹來看就是從葉尋根的方式來工作的,當完全符合末一種規(guī)則時,就會完成譯碼,將PLC指令中對應的地址信息存儲到定義的目標代碼緩沖區(qū)中,完成一條語句表的譯碼。以下是布爾型操作數(shù)指令對應的語法樹,如圖1所示。
生成的是滿足C語言格式bison_plc.tab.h和bison_plc.tab.c文件。最后,將Flex_plc.c、bison_plc.tab.h和bison_plc.tab.c文件一同放在工程目錄下,供開發(fā)系統(tǒng)中的編譯菜單的響應函數(shù)OnCompile( )調用,實現(xiàn)指令表的編譯功能。
根據(jù)上述方法,可以生成獨立運行的詞法分析器和語法分析器,也可以將二者結合起來,由詞法分析器識別單詞,傳遞給語法分析器處理。本文采用語法分析器調用詞法分析程序,即將它們結合的方法,在一個工程中完成詞法分析和語法分析模塊。
Bison所生成的語法分析程序yyparse()調用的詞法分析程序與flex所生成的程序都是 yylex()函數(shù)。對于Flex生成的詞法分析器,和Bison結合使用時,每當yylex()讀取并匹配了一個模式時,就返回一個標記,語法分析程序獲得返回的標記后,進行語法分析。當Bison運行一個帶有標記的.y文件時,會生成一個頭文件,它對每個標記都有#define的定義,這個頭文件必須在相應的Flex源文件中的C聲明段中包含。
Flex與Bison結合生成編譯器的步驟為[6]:
1)根據(jù)指令表的編寫格式和規(guī)則,編寫一個名為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文件。
執(zhí)行命令為:..bison-d Bison_plc.y
3)運行Flex_plc.l文件,生產Flex_plc.yy.c的c語言文件。執(zhí)行命令為:..flex Flex_plc.l
4)用Visual C++將Bison_plc.tab.c、.Bison_plc.tab.h和 Flex_plc.yy.c連接起來編譯,生成可執(zhí)行文件Flex_plc.yy.exe。
5)運行Flex_plc.yy.exe,即可對指令表語言進行詞法和語法分析。
圖2 Flex與Bison的結合
代碼轉換在詞法分析、語法分析完成后,如沒有錯誤 ,語法分析程序輸出目標代碼。軟PLC開發(fā)系統(tǒng)的目標代碼是PLC指令的二進制編碼[7],例如:操作符AND的內部碼為00FF00,元件X的編碼0X01,將操作符的編碼與元件編碼相加作為目標代碼的高十六位,低十六位為元件的編號,指令AND X1的編碼為00FF010001。代碼轉換的過程在詞法分析時完成。
應用Flex和Bison編寫軟PLC的編譯模塊,它的優(yōu)點是與用戶自己手動編寫詞法與語法分析程序相比,可以減少大量的編碼,并且速度和準確度都有很大提高。如果目前的指令系統(tǒng)發(fā)生變化,規(guī)則需要擴充或修改時,對于Flex和Bison程序來說只需修改很少的部分即可,這種易于維護和實現(xiàn)的方案不僅降低了軟PLC編譯器的開發(fā)難度也提高了效率。
[1] 基于MSYS的 Flex & Bison(編譯器開發(fā)工具)使用教程.http://www.360doc.com/content/10/0424/13/1156733_246 48165.shtml.
[2] 田文琦,于東,高偉,紀元.flex和bison在軟PLC編譯器中的應用[J].微計算機信息,2009,7-1:235-237.
[3] 陳火旺,錢家嘩,孫永強.編譯原理[M].國防工業(yè)出版社,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++集成開發(fā)環(huán)境下應用flex和bison開發(fā)編譯器[J].戰(zhàn)術導彈控制技術.2004,(4):64-67.
[7] 張少坤,游有鵬.基于Lex& Yacc的PLC指令編譯器的研究與實現(xiàn)[J].PLC&FA,34-35.