【正文】
}?;驹瓌t是在代碼生成中忽略,而在語義分析中檢查。在遞歸過程中具體的實現(xiàn)代碼和子節(jié)點調(diào)用方面的特別處理由genCode函數(shù)根據(jù)節(jié)點T的屬性進(jìn)行區(qū)分。end。generate code to implement the action of T。genCode (2nd child of T ) 。beginif T is not nil thenbegingenerate code to implement the action of T。 代碼生成的基本思路代碼生成的基本思路是:采用通用的對語法樹進(jìn)行遍歷的方式生成目標(biāo)代碼[36]。以一個典型的TTCN3 Module為例,其編譯結(jié)果可能包含以下文件: // 模塊定義部分,數(shù)據(jù)類型和常量等定義 // 模塊控制部分,控制測試?yán)膱?zhí)行,// 包含main()函數(shù) // 測試?yán)?的定義 // 測試?yán)?的實現(xiàn) // 測試?yán)?的定義 // 測試?yán)?的實現(xiàn)…除此以外,還將包含兩個文件:,它是編譯器提供的公用文件,包含以上文件所需的類型定義、全局變量和庫函數(shù)等。之所以采用C++而不是標(biāo)準(zhǔn)C,是為了簡化代碼生成的算法,增強(qiáng)目標(biāo)代碼的結(jié)構(gòu)性和可讀性,而且現(xiàn)在絕大多數(shù)的C編譯和集成開發(fā)工具都能支持C++編譯。而本編譯器的源語言(TTCN3)和目標(biāo)語言(C語言)都屬于或接近高級語言,不具有規(guī)則形式,因此本編譯器沒有選擇或設(shè)計中間代碼,而直接從語法樹和符號表生成C代碼。通常以匯編語言或機(jī)器碼為目標(biāo)代碼的編譯程序需要某種形式的中間代碼,這是因為大多數(shù)機(jī)器的指令集都具有運(yùn)算符加1至2個運(yùn)算碼的規(guī)則形式,用中間代碼可以方便目標(biāo)代碼的生成[36]。經(jīng)過反復(fù)修改和重復(fù)第一遍掃描過程,獲得詞法、語法和語義都正確的用TTCN3描述的測試控制數(shù)據(jù),這就為第二遍掃描生成C代碼奠定了基礎(chǔ)。如果某個節(jié)點因為語義分析未通過而導(dǎo)致其屬性不確定,則會引起其上面的系列節(jié)點語義錯誤。...Semantic error at line 5: name clash, multiple declarations...Semantic error at line 6: no declarations for identifier c...Semantic error at line 7: type mismatch, cannot INTEGER_TYPE FLOAT_TYPE...Semantic error at line 18: the type of the actual parameter does not match the declared formal parameter...Semantic error at line 20: the type of the expression is not correct...Semantic error at line 21: the type of NetworkNode is not correct如果順利通過了語義檢查,則提供了完整的符號表供后續(xù)編譯階段使用。 (9)。 (1)。 } testcase case() runs on NetworkNode { MyBehaviourA(2,)。} } type port IPPortType mixed { inout integer。 // 重復(fù)聲明 c:=a+b。例如以下一小段TTCN3程序,注釋是標(biāo)明語義出錯的地方。由于TTCN3具有名字唯一性,且不允許全局變量定義等特點[16],使得本編譯器的作用域?qū)傩员容^簡單,僅包括全局、本地和參數(shù)三種作用域。標(biāo)識符很重要的一個屬性就是作用域。TTCN3主要有六大類聲明[16],且各類聲明都有各自的屬性特點,因此要在結(jié)構(gòu)體中分別定義這幾類聲明的屬性特點。} *SEntry。 /* 為本地聲明新建的鏈表 */ } testcase。 SEntryLIST formals。 /* 由于TTCN3不可以聲明全局變量,所以變量聲明中沒有作用域?qū)傩?*/ } var。 /* 常量聲明的作用域?qū)傩?*/ } const。 /* 標(biāo)明標(biāo)識符的類型屬性 */ union /* 聯(lián)合了八類聲明的屬性 */ { struct { /* 該結(jié)構(gòu)體定義了常量聲明的屬性 */ int offset。 /* 記錄標(biāo)識符的名字屬性 */ enum SEntry_kind use。其中結(jié)構(gòu)Entry是符號表項的數(shù)據(jù)結(jié)構(gòu),包括符號的名字及屬性。 struct node * next。散列表的每一項都是一個單獨(dú)的鏈表SEntryList,SEntryList的每個節(jié)點包括指向表項Entry的指針sentry,和指向下一個節(jié)點的指針next。 return h % MAX_TAB_SIZE。散列函數(shù)是:static unsigned int hash_function(char *lexeme) { unsigned int h = 0。對符號表的操作非常頻繁,其效率直接影響到編譯程序的效率,因此符號表的組織非常重要。如果存在則根據(jù)需要讀取或更新其信息;如果不存在,則添加入口,將此名字及相關(guān)信息登記在符號表中。這些信息將用于語義檢查、代碼生成等不同階段。這些信息通常記錄在符號表[36]中。歸約是自底向上的,只要將每次歸約并成功通過語義檢查的節(jié)點屬性記錄下來,再向上繼續(xù)歸約時就可以利用這些屬性,而無需再遞歸地去逐級向下檢查直到葉節(jié)點。在識別出語法結(jié)構(gòu)后進(jìn)行即時處理的嵌入式代碼中,在生成新節(jié)點的同時,根據(jù)語義規(guī)則和屬性值對新節(jié)點與其子節(jié)點在語義上能否構(gòu)成一棵樹進(jìn)行檢查。本編譯器采用兩遍掃描的方式,語義分析與詞法和語法分析都在一遍之內(nèi)完成,也是通過YACC的嵌入式動作來實現(xiàn)的。 在YACC中實現(xiàn)語義分析語義分析的工作包括類型檢查、控制流檢查、一致性檢查、相關(guān)名字檢查等[36]。語義的闡明要比語法的闡明難得多,至今還沒有比較公認(rèn)的形式系統(tǒng)可以用來構(gòu)造適用的語義分析器。語義就是一組說明詞法和語法結(jié)構(gòu)含義的規(guī)則,通過它可以定義一個程序的意義。 語法分析的錯誤處理我們沿用YACC默認(rèn)對語法分析的錯誤處理,就是當(dāng)發(fā)現(xiàn)語法錯誤就停下來,并指出錯誤的位置。 ()。例如,輸入以下一段TTCN3程序段:module ND_Behavior { import from ND_Architecture recursive all。不同的語法規(guī)則嵌入的語句也不同,對所有的語法規(guī)則都嵌入各自相應(yīng)的語句,就可以構(gòu)造出語法樹。以上一段程序中,$$ = newTreeNode(SINGLEVERINS)表明要新建立一個樹節(jié)點(,它分配一個新的樹節(jié)點,同時返回一個指向新分配的節(jié)點的指針)。 }。 $$child[0] = $1。 $$child[2] = $4。 $$child[0] = $1。例如以下一段YACC中定義的TTCN3語法規(guī)則:SingleVarInstance : Identifier optArrayDef ASSIGN VarInitialValue | Identifier optArrayDef 。首先,在YACC的說明部分中包含上述頭文件:include ;并且加入定義: define YYSTYPE TreeNode *它定義了YACC分析程序返回的類型是指向樹節(jié)點TreeNode的指針。LEX和YACC都允許嵌入式動作,就是在LEX或YACC的源程序中嵌入宿主語言(C語言)的代碼,以實現(xiàn)詞法結(jié)構(gòu)或語法規(guī)則匹配后的即時處理。// 記錄節(jié)點的類型} TreeNode。 // 記錄語法樹節(jié)點的名字 char *sval。 // 簿記屬性,它允許在出現(xiàn)分析錯誤時能夠打印// 源代碼所在行數(shù) TokenType op。// MAXCHILDREN定義// 為5,每個樹節(jié)點// 最多可以有5個子// 節(jié)點 struct treeNode * sibling。exp+opexpexp434+3圖35 表達(dá)式3+4的語法樹圖34 表達(dá)式3+4的分析樹 語法樹的結(jié)構(gòu),如下所示C程序段。第一遍掃描源程序,對其進(jìn)行詞法、語法和語義分析,并輸出語法樹;第二遍掃描的是第一遍輸出的語法樹,對語法樹進(jìn)行遍歷來完成代碼生成。需要說明的是,分析樹和語法樹在本質(zhì)上是相同的。在語法樹中,運(yùn)算符和關(guān)鍵字不再是葉節(jié)點,而是作為內(nèi)部節(jié)點成為分析樹中葉節(jié)點的父節(jié)點。例如表達(dá)式3 + 4的分析樹如圖34所示。分析樹[36]是表述單詞符號串結(jié)構(gòu)的一種十分有用的表示法。使用上述3種辦法,較好地解決了YACC出現(xiàn)的沖突問題,實現(xiàn)了正確的TTCN3語法分析器。optOutParKeyword : OUT | /* 空 */。optInParKeyword : IN | /* 空 */。如在以下的語法規(guī)則定義中,由于optInParKeyword,optInOutParKeyword ,optOutParKeyword這三個產(chǎn)生式中有同類項‘空’,因此會引起大量的規(guī)約/規(guī)約沖突。這一方法廣泛使用在書寫TTCN3的通信操作語句中,如SEND,CALL,RECEIVE,TRIGGER等等。其實產(chǎn)生這類沖突的深層原因在于YACC可以向前查看的字符數(shù)為1個[50]。(ok)時,首先讀入字符IPPort,此時既可以根據(jù)產(chǎn)生式Port : ID來規(guī)約,也可以根據(jù)產(chǎn)生式ValueReference : ID ‘.’ ID來移進(jìn)下一個字符‘.’,于是就出現(xiàn)了一個移進(jìn)/規(guī)約沖突。Port : ID| ID ArrayOrBitRef。經(jīng)測試,該沖突致使語法分析器無法識別SEND類語句,(ok)。2) 盡量減少嵌套。ElseClause : ELSE StatementBlock| ELSE ConditionalConstruct。把optElseIfClauses規(guī)則和optElseClause規(guī)則合并,改成以下的寫法:ConditionalConstruct : IF ‘(‘ BooleanExpression ‘)’] StatementBlock optElseClause。經(jīng)分析,該沖突產(chǎn)生的原因在于:當(dāng)YACC得到輸入流中的ELSE關(guān)鍵字時,它無法確定是應(yīng)該移進(jìn)optElseIfClauses規(guī)則中的ELSE還是移進(jìn)optElseClause 中的ELSE,二者有相同的可移進(jìn)字符ELSE,于是產(chǎn)生了沖突。OptElseClause : ElseClause| /* 空 */。ElseIfClauses : ElseIfClauses ElseIfClause| ElseIfClause。ConditionalConstruct : IF‘(’BooleanExpression‘)’ StatementBlockoptElseIfClauses optElseClause 。如在YACC的語法規(guī)則部分中定義TTCN3的條件語句時,如果根據(jù)BNF的格式書寫,如下所示的程序,YACC將會提示產(chǎn)生了沖突。 減少沖突的辦法改變某些語法規(guī)則的寫法可以有效地消除某些沖突,或雖未消除某些沖突,但改變了這些沖突的形式,使YACC能夠通過默認(rèn)的沖突處理規(guī)則正確地分析TTCN3語法結(jié)構(gòu)。 規(guī)約/規(guī)約沖突的解決是從發(fā)生沖突的產(chǎn)生式中選擇在YACC說明中最先出現(xiàn)的那個產(chǎn)生式。“X”既可能是proga,也可能是progb,二者都想規(guī)約,于是就出現(xiàn)了規(guī)約/規(guī)約沖突。proga : ‘X’ 。當(dāng)同樣的標(biāo)記可以完成兩個不同的規(guī)則時就會發(fā)生規(guī)約/規(guī)約沖突。對于輸入串“X+X+X”,有兩種可能的分析:“(X+X)+X”或“X+(X+X)” 。當(dāng)一個輸入字符串有兩種可能的分析,而且其中一個分析完成了一個規(guī)則(規(guī)約選項),而另一個卻沒有(移進(jìn)選項)時,就會產(chǎn)生移進(jìn)/規(guī)約沖突。解決沖突問題是語法分析實現(xiàn)的難點和關(guān)鍵。詞法規(guī)則語法規(guī)則YACCLEX輸入串語法分析輸出單詞符號串yyparseyylex 圖33 LEX和YACC的關(guān)系 語法規(guī)則的沖突處理由于TTCN3的BNF很龐大、TTCN3語言本身存在二義性、以及YACC向前查看的字符數(shù)僅為1個等原因,導(dǎo)致了YACC在生成語法分析器時會遇到大量的沖突問題。YACC源程序YACC編譯系統(tǒng)yyparse輸入串yyparse語法樹/分析樹圖32 YACC編譯系統(tǒng)的作用 與詞法分析的結(jié)合在本編譯器中,語法分析和詞法分析在一遍掃描內(nèi)完成,詞法分析被看作是語法分析的一個子程序,當(dāng)語法分析需要輸入單詞符號時就調(diào)用詞法分析。YACC的源程序的格式如下[38]:說明部分%%語法規(guī)則部分%%程序段部分本編譯器中,YACC源程序的說明部分包括了用來建立分析程序的