現代編譯原理——第四章:語義分析以及原始碼
轉自: http://www.cnblogs.com/BlackWalnut/p/4527845.html
寫完語義分析的程式碼後感覺語義分析只是為了進行型別檢測(後來才發現,這只是語義分析的一部分)。詞法分析注重的是每個單詞是否合法,以及這個單詞屬於語言中的哪些部分。語法分析的上下文無關文法注重的是一個一個的推導式,是將詞法分析中得到的單詞按照語法規則進行組合。那麼,語義分析就是要了解各個推導式之間的關係是否合法,主要體現在推導式中使用的終結符和非終結符之間的關係,也就是它們的型別。所以語義分析注重的一個方面是型別檢測。
為了將上下文無關文法中各種終結符和非終結符聯絡起來,以及在想要使用它們的時候得到它們相應的型別,我們使用了一種叫做符號表的東西,又稱為環境。環境可以理解為對每一個ID建立一個棧,棧中存放的和這個ID相關的一些資訊,這些資訊稱為繫結。使用棧的好處是,可以解決作用域的問題。ID始終對應於棧頂的繫結,每次進入一個新的作用域就將一個作用域標示符壓入棧中。這樣,在一個新的作用域中定義了一個和老作用域ID相同的變數,型別或者函式時,將新的繫結壓入棧中,那麼老的繫結就會失效。當出作用域時,將作用域標示符以上的所有繫結都彈出,這樣就完成了老繫結的恢復。
上面介紹的棧式環境,我們稱為命令式風格。還有一種稱為函式式風格,它的特點是每次都將原來的環境複製一份,將老的儲存起來,對新的進行更改。當出作用域時,直接將新的拋棄,然後使用老的。
為什麼只存放變數,型別以及函式的相關資訊呢?我們可以看到,一種語言其實包含四個部分型別宣告,函式宣告,變數宣告以及表示式, 前三個就利用內建型別來創造新的東西,而表示式則是使用這些東西所要遵守的規則,這些規則都是由上下文無關文法定義好的。宣告和表示式就組成了一個語言的基本部分,我們只能使用這些規則來組織我們創造的東西,最終形成我們的程式。所以在語義分析階段,我們只用注重各種型別的檢測,看看在特定的規則下是否符合要求。
下面我們來看看tiger中語義分析時要注意的事項。
在tiger的語義分析過程中,我們使用了兩個環境,值環境和型別環境。其中,值環境用來存放函式宣告,變數宣告的,型別環境是用來存放型別宣告的。之所以使用兩個符號表是因為在tiger中我們允許型別和變數,函式名使用相同的ID,但是不允許變數和函式使用相同的ID。並且,在tiger中,為了更快的找到ID所對應的繫結,我們使用了hash表來儲存每個ID對應的棧。但是這裡有個問題要解決,就是hash可能會造成衝突。例如ID1和ID2同時hash到同一個表項中,那麼這個表項對應的棧是誰的?這個時候,我們要在繫結中存放這個繫結對應的ID。我們只要從棧頂開始比較,找到第一個和我們hash的ID相同的繫結,就是我們需要的繫結。
但是,這樣的話,每次查詢hash表都要進行字串的比較,是十分低效的。所以,tiger中symbol.h檔案給了另一個hash表,這個hash表的作用是將一個ID對映到一個指標(就是在語法分析中使用函式S_symbol)。那麼我們只要將這個指標和繫結壓入棧總,比較這個指標就可以確定這個繫結是不是我需要查詢的繫結。這是一個十分精巧的設計。
知道了如何根據ID查詢相應的繫結,那麼我們來看看這些繫結究竟是什麼。
首先,我們向棧中壓入繫結時,繫結其實使用一個void*型別的指標指向的,也就是說,我們不關心壓入的繫結是什麼,我們只關心壓入繫結的地址以及壓入到哪個表中(函式s_enter)。在讀出(函式s_look)繫結的時候,我們只要把這個地址轉換為我們需要的繫結(值繫結或者型別繫結)就可以了。
對於型別環境,每一個型別ID對應一個Ty_ty_結構體定義的繫結,這個Ty_ty_在Types.h中定義。可以看出這個結構體包含的型別(kind 列舉)是很多,其中要注意的是TY_name這個列舉,這個列舉是所有由 type id = id 這種語句定義的型別。根據不同的型別,我們使用聯合中不同變數來解讀。後面的幾個函式都是根據不同kind來建立不同Ty_ty。但是要注意到,在新建一個型別環境的時候,我們要把int和string兩個ID以及對應的繫結壓入(S_enter)棧中,這兩個是內建型別,必須先入棧。這裡的int和string是型別,也就是終結符ID。和詞法分析時的INT STRING的概念不同,這兩個INT STRING是整型常量和字元型常量,它們的型別是int和string。
對於值環境,我們使用env.h中的E_enventry 結構體。這個就相對簡單許多。
知道tiger中使用的繫結是什麼樣子的,就來說說這裡面的一些坑。注意到我們使用到很多指標,大部分都是由指標引起的。
首先,我們在進行型別比較的時候,使用的指標。這裡說說為什麼,對於 type intary = array of int 以及 type strary = array of string ,如果只intary和strary所對應的繫結的kind來判斷,兩個都是Ty_array,但是兩個的型別確實是不一樣的,所以這種只使用kind判斷的方式是失效的。所以這個時候我們要看看繫結中聯合裡的array(這是一個指標)是否一致。我們檢視內建型別(int vaid nil sting),它們都是由函式(Ty_Int等)直接返回的指標,檢視函式後發現,這些內建型別使用的都是相同的地址。但是其他型別的地址卻有可能不同,這時就是下面這個注意事項。
另外一個需要注意的是,因為在tiger中存在這樣的型別定義 type ID1 = ID2 也就是說ID1 是 ID2 的別名,此時ID1中的繫結指向由 Ty_Name 函式返回的一個地址,該函式申請了一塊新的記憶體。如果我們再定義type ID3 = ID2 ,此時使用指標比較ID1 和 ID3,這個時候判斷兩個型別是不一致的。這顯然和tiger的要求的相違背。這個時候我們定義了一個新的函式actrulyTy,這個函式將返回繫結的“真實型別”,這些真是型別只可能是四個內建型別 ,陣列型別或者記錄型別,同時書中要求返回的任何expty中的Ty_ty必須是“真實型別”。那麼,對一下程式碼進行型別檢測就可以得到正確的結果。
type recd = { a : int , b : string } type recd1 = recd type recd2 = recd //測試 recd1 和 recd2 是否相等 recd1 == recd2
這裡提醒一下,我提供的程式碼實不支援一下型別檢測的
type recd = { a : int , b : string } type recd1 = recd type recd2 = recd type recd1ary = array of recd1 type recd2ary = array of recd2
//以下此時將返回false
recd1ary == recd2ary
其實就是將recd1和recd2 再次進行一次actrulyTy就可以了。總之,兩型別比較時,一定要求時“真實型別”。
還有一個,可能是虎書的作者沒有注意到的一個地方(或許是我的程式碼有問題??)。在使用詞法分析器向語法分析器傳送ID以及相應的字串時,我們使用一個變數(在我上一篇文章中講述bison和flex傳值)yylval.sval ,注意,這個sval是一個指標,指向一個字串的開頭,被指向的這個字串是yytext。這個yytext字串在進行詞法分析時是會改變的。所以當你在語法分析器中將詞法分析器傳出的sval作為引數呼叫S_symbol時,這個sval指向的字串yytext可能已經改變了(因為語法分析器存在移進以及規約,所以並不是和詞法分析器同步工作的)。因此在使用s_symbol時要進行一些調整,如下:
S_symbol S_Symbol(string Id) { int i = 0 ; for ( ; (Id[i] >= '0' && Id[i] <= '9') || ( Id[i] >= 'a' && Id[i] <= 'z')||( Id[i] >= 'A' && Id[i] <= 'Z') ;++i) ; string name = (string)checked_malloc( sizeof(*name) * (i+1)) ; int b = sizeof(*name) * (i+1) ; memcpy(name , Id , sizeof(*name)*(i)) ; name[i] = '\0' ; int index= hash(name) % SIZE; S_symbol syms = hashtable[index], sym; for(sym=syms; sym; sym=sym->next) if (streq(sym->name,name)) return sym; sym = mksymbol(name,syms); hashtable[index]=sym; return sym; }
程式碼比較渣渣。。。。同樣的調整還出現在處理字串常量的函式中。
對於要處理的函式以及記錄型別的遞迴,可以看成時c++中先處理標頭檔案,在處理cpp。也不算太難,但是要注意,保持指標指向正確的位置。
以下就是這次的部分程式碼,沒有實現當有錯誤時,顯示錯誤位置的功能。有些程式碼比較簡單,就沒有貼出來。這份程式碼已經經過隨書附帶的前六個測試用例測試過,沒有問題:
env.h
#ifndef ENV_H_ #define ENV_H_ #include "types.h" typedef struct E_enventry_ *E_enventry ; struct E_enventry_ { enum { E_varEntry , E_funEntry } kind; union { struct { Ty_ty ty ; } var; struct { Ty_tyList formals ; Ty_ty result ;} fun ; }u; }; E_enventry E_VarEntry(Ty_ty ty) ; E_enventry E_FunEntry(Ty_tyList formals , Ty_ty reslut) ; S_table E_base_tenv() ; S_table E_base_venv() ; Ty_ty actrulyTy(Ty_ty) ; bool isTyequTy( const Ty_ty ,const Ty_ty) ; void tyCpy(Ty_ty dec , const Ty_ty src ); #endif
env.cpp
#include "env.h" #include <stdio.h> #include <string.h> E_enventry E_VarEntry(Ty_ty ty) { E_enventry p = (E_enventry) checked_malloc(sizeof(*p)) ; p->kind = E_enventry_::E_varEntry ; p->u.var.ty = ty ; return p ; } E_enventry E_FunEntry(Ty_tyList formals , Ty_ty reslut) { E_enventry p = (E_enventry)checked_malloc(sizeof(*p)) ; p->kind = E_enventry_::E_funEntry ; p->u.fun.formals = formals ; p->u.fun.result = reslut ; return p ; } Ty_ty actrulyTy(Ty_ty ty) { if (ty == NULL ) { return NULL ; } while(ty->kind == Ty_ty_::Ty_name) { ty = ty->u.name.ty ; } return ty ; } bool isTyequTy(const Ty_ty s1 , const Ty_ty s2) { Ty_ty tmp1 = actrulyTy(s1) ; Ty_ty tmp2 = actrulyTy(s2) ; bool aryOrRec = (tmp1->kind == Ty_ty_::Ty_array || tmp2->kind == Ty_ty_::Ty_record) ; bool isnil = (tmp1->kind == Ty_ty_::Ty_nil || tmp2->kind == Ty_ty_::Ty_nil) ; if ( tmp1->kind != tmp2->kind) { if ( isnil && aryOrRec ) { return true ; } return false ; } if (aryOrRec) { if (tmp1 != tmp2 ) { return false ; } } return true ; } void tyCpy(Ty_ty dec , const Ty_ty src) { if (dec == NULL || src == NULL ) { assert(0) ; } memcpy(dec , src , sizeof(*src)) ; }
sement.h
#ifndef SENMANT_H_ #define SENMANT_H_ #include "types.h" #include "translate.h" #include "absyn.h" struct expty { Tr_exp exp ; Ty_ty ty; }; expty expTy(Tr_exp exp , Ty_ty ty) ; expty transVar(S_table venv , S_table tenv , A_var var) ; expty transExp(S_table venv , S_table tenv , A_exp exp) ; void transDec(S_table venv , S_table tenv , A_dec dec) ; Ty_ty transTy( S_table tenv , A_ty ty) ; bool innerIdentifiers( S_symbol sym); #endif
sement.cpp
#include "semant.h" #include <assert.h> #include "env.h" expty expTy(Tr_exp exp , Ty_ty ty) { expty e ; e.exp = exp ; e.ty = ty ; return e ; } expty transExp(S_table venv , S_table tenv , A_exp exp) { if (exp == NULL ) { assert(0); } switch(exp->kind) { case A_varExp : return transVar(venv , tenv , exp->u.var) ; case A_nilExp : return expTy(NULL ,Ty_Nil()); case A_intExp : return expTy(NULL , Ty_Int()) ; case A_stringExp : return expTy(NULL , Ty_String()) ; case A_callExp : { E_enventry tmp = (E_enventry)S_look(venv, exp->u.call.func) ; if (tmp == NULL) { assert(0) ; } Ty_tyList tylist = tmp->u.fun.formals ; A_expList explist = exp->u.call.args ; while (tylist != NULL && explist != NULL) { expty exptyp = transExp(venv , tenv , explist->head) ; if (exptyp.ty->kind == Ty_ty_::Ty_nil) { continue ; } if (!isTyequTy(tylist->head , exptyp.ty)) { assert(0) ; } tylist = tylist->tail ; explist = explist->tail ; } if (tylist != NULL || explist != NULL ) { assert(0); } return expTy(NULL , actrulyTy(tmp->u.fun.result)) ; } case A_opExp : { switch(exp->u.op.oper) { case A_plusOp : case A_minusOp : case A_timesOp : case A_divideOp : case A_ltOp : case A_leOp : case A_gtOp : case A_geOp : { if (transExp(venv , tenv, exp->u.op.left).ty->kind != Ty_ty_::Ty_int) assert(0); if (transExp(venv , tenv, exp->u.op.right).ty->kind != Ty_ty_::Ty_int) assert(0); return expTy(NULL , Ty_Int()) ; } case A_eqOp : case A_neqOp: { expty tmpleft = transExp(venv , tenv, exp->u.op.left) ; expty tmpright = transExp(venv , tenv, exp->u.op.right) ; if (tmpleft.ty->kind == Ty_ty_::Ty_int && tmpright.ty->kind == Ty_ty_::Ty_int) return expTy(NULL , Ty_Int()) ; if (tmpleft.ty->kind == tmpright.ty->kind) { if (tmpleft.ty->kind == Ty_ty_::Ty_record || tmpleft.ty->kind == Ty_ty_::Ty_array) { if ( tmpleft.ty == tmpright.ty ) { return expTy(NULL , Ty_Int()) ; } } } assert(0); } } assert(0); } case A_recordExp : { Ty_ty tmpty = (Ty_ty)S_look(tenv , exp->u.record.typ) ; tmpty = actrulyTy(tmpty) ; if (tmpty == NULL ) { assert(0) ; } if (tmpty->kind != Ty_ty_::Ty_record ) { assert(0) ; } A_efieldList tmpefield = exp->u.record.fields ; Ty_fieldList tmpfieldlist = tmpty->u.record ; while(tmpefield && tmpfieldlist) { if (tmpefield->head->name != tmpfieldlist->head->name ) { assert(0) ; } if (!isTyequTy(transExp(venv , tenv , tmpefield->head->exp).ty ,tmpfieldlist->head->ty)) { assert(0) ; } tmpefield = tmpefield->tail ; tmpfieldlist = tmpfieldlist->tail ; } if (tmpfieldlist!= NULL || tmpefield != NULL ) { assert(0) ; } return expTy(NULL ,tmpty); } case A_seqExp : { A_expList explist = exp->u.seq ; if (explist) { while(explist->tail) { transExp( venv , tenv , explist->head); explist = explist->tail ; } } else { return expTy(NULL , Ty_Void()); } return transExp(venv , tenv , explist->head); } case A_assignExp : { expty tmpV = transVar(venv , tenv , exp->u.assign.var) ; expty tmpE = transExp(venv , tenv , exp->u.assign.exp); if (tmpE.ty->kind != tmpV.ty->kind) { assert(0); } return expTy(NULL , Ty_Void()); } case A_ifExp : { expty tmptest = transExp(venv ,tenv , exp->u.iff.test) ; if(tmptest.ty->kind != Ty_ty_::Ty_int) { assert(0); } expty tmpthen = transExp(venv , tenv , exp->u.iff.then) ; if (exp->u.iff.elsee != NULL) { expty tmpelse = transExp(venv , tenv , exp->u.iff.elsee) ; if ( tmpthen.ty != tmpelse.ty ) { assert(0); } return expTy(NULL , tmpelse.ty); } if (tmpthen.ty->kind != Ty_ty_::Ty_void) { assert(0); } return expTy(NULL , Ty_Void()); } case A_whileExp : { expty test = transExp(venv , tenv , exp->u.whilee.test); if (test.ty->kind != Ty_ty_::Ty_int) { assert(0) ; } expty body = transExp(venv , tenv , exp->u.whilee.body); if (body.ty->kind != Ty_ty_::Ty_void) { assert(0); } return expTy(NULL , Ty_Void()); } case A_forExp : { expty tmplo = transExp(venv , tenv , exp->u.forr.lo); expty tmphi = transExp(venv , tenv , exp->u.forr.hi); S_beginScope(venv); S_enter(venv , exp->u.forr.var , E_VarEntry(Ty_Int())); expty tmpbody = transExp(venv , tenv, exp->u.forr.body); if (tmplo.ty->kind != Ty_ty_::Ty_int || tmphi.ty->kind != Ty_ty_::Ty_int || tmpbody.ty->kind != Ty_ty_::Ty_void) { assert(0); } S_endScope(venv); return expTy(NULL , Ty_Void()); } case A_breakExp : return expTy(NULL , Ty_Void()); case A_letExp : { S_beginScope(venv); S_beginScope(tenv); A_decList declist = exp->u.let.decs ; while(declist != NULL) { transDec(venv , tenv , declist->head); declist = declist->tail; } expty tmp ; if (exp->u.let.body) { tmp = transExp(venv , tenv , exp->u.let.body); } else { tmp = expTy(NULL , Ty_Void()) ; } S_endScope(venv); S_endScope(tenv); return tmp ; } case A_arrayExp : { Ty_ty ty = (Ty_ty)S_look(tenv , exp->u.array.typ); ty = actrulyTy(ty); if (ty == NULL || ty->kind != Ty_ty_::Ty_array) { assert(0); } expty tynum = transExp(venv , tenv , exp->u.array.size); if (tynum.ty->kind != Ty_ty_::Ty_int) { assert(0); } expty tyinit = transExp(venv , tenv, exp->u.array.init) ; if (tyinit.ty != ty->u.array ) { assert(0) ; } return expTy(NULL , ty); } } assert(0); } expty transVar(S_table venv , S_table tenv , A_var var) { switch(var->kind) { case A_simpleVar : { E_enventry tmp = (E_enventry) S_look(venv , var->u.simple) ; if (tmp != NULL && tmp->kind == E_enventry_::E_varEntry) { return expTy(NULL , actrulyTy(tmp->u.var.ty)) ; } assert(0) ; } case A_fieldVar : { expty tmpty = transVar(venv , tenv , var->u.field.var) ; if (tmpty.ty->kind != Ty_ty_::Ty_record) { assert(0); } Ty_fieldList fieldList = tmpty.ty->u.record ; while( fieldList ) { if ( fieldList->head->name == var->u.field.sym ) { return expTy(NULL , actrulyTy(fieldList->head->ty)) ; } fieldList = fieldList->tail ; } assert(0); } case A_subscriptVar : { expty tmp = transVar(venv , tenv , var->u.subscript.var) ; if (tmp.ty->kind != Ty_ty_::Ty_array ) { assert(0) ; } expty tmpexp = transExp(venv , tenv , var->u.subscript.exp) ; if (tmpexp.ty->kind != Ty_ty_::Ty_int) { assert(0) ; } return tmp ; } } assert(0) ; } void transDec(S_table venv , S_table tenv , A_dec dec) { switch(dec->kind) { case A_functionDec : { A_fundecList tmpfun = dec->u.function ; while(tmpfun) { A_fieldList tmpfeldList = tmpfun->head->params ; Ty_tyList tylist = NULL ; while(tmpfeldList) { Ty_ty ty = (Ty_ty)S_look(tenv,tmpfeldList->head->typ); tylist = Ty_TyList(ty , tylist) ; tmpfeldList = tmpfeldList->tail ; } if (innerIdentifiers(tmpfun->head->name)) { assert(0) ; } //¿‡À∆”⁄…˘√˜“ª∏ˆ∫Ø ˝ ªπ√ª”–∂®“ÂÀ¸ S_enter(venv , tmpfun->head->name , E_FunEntry(tylist , (Ty_ty)S_look(tenv ,tmpfun->head->result))) ; tmpfun = tmpfun->tail ; } tmpfun = dec->u.function ; while(tmpfun) { S_beginScope(venv) ; A_fieldList tmpfeldList = tmpfun->head->params ; while(tmpfeldList) { Ty_ty ty = (Ty_ty)S_look(tenv,tmpfeldList->head->typ); if (innerIdentifiers(tmpfeldList->head->name)) { assert(0); } S_enter(venv ,tmpfeldList->head->name, E_VarEntry(ty)) ; tmpfeldList = tmpfeldList->tail ; } transExp(venv , tenv , tmpfun->head->body) ; S_endScope(venv) ; tmpfun = tmpfun->tail ; } return ; } case A_typeDec : { A_nametyList namelist = dec->u.type ; while(namelist) { if (innerIdentifiers(namelist->head->name)) { assert(0) ; } // ¥¶¿Ìµ›πÈ ¿‡À∆”⁄ …˘√˜“ª∏ˆ¿‡–Õ µ´ «ªπ√ª”–∂®“ÂÀ¸ S_enter(tenv , namelist->head->name ,Ty_Name(namelist->head->name , NULL)) ; namelist = namelist->tail ; } namelist = dec->u.type ; while(namelist) { // ¥¶¿Ìµ›πÈ Ty_ty tmp1 = transTy(tenv , namelist->head->ty ) ; Ty_ty tmp2 = (Ty_ty)S_look(tenv , namelist->head->name) ; if ( tmp1->kind == Ty_ty_::Ty_int || tmp1->kind == Ty_ty_::Ty_string || tmp1->kind == Ty_ty_::Ty_nil || tmp1->kind == Ty_ty_::Ty_void) { //如果是內建型別 繫結指向的地方是一個固定的地方 所以 這個時候就不是替換內容那麼簡單了 tmp2 = (Ty_ty)S_changeBind(tenv , namelist->head->name , tmp1); tmp2 = (Ty_ty)freeTy(tmp2) ; } else { tyCpy(tmp2 , tmp1) ; tmp1 = (Ty_ty)freeTy(tmp1) ; } namelist = namelist->tail ; } namelist = dec->u.type; while(namelist) { // 避免出現 type a = b type b = a 這種沒有真實型別的型別定義 Ty_ty tmp = (Ty_ty)S_look(tenv , namelist->head->name) ; if (!actrulyTy(tmp)) { assert(0) ; } namelist = namelist->tail ; } return; } case A_varDec : { if(dec->u.var.init == NULL) { assert(0) ; } expty tmp = transExp(venv , tenv , dec->u.var.init) ; if( (dec->u.var.typ != NULL) ) { if ( actrulyTy((Ty_ty)S_look(tenv ,dec->u.var.typ)) != tmp.ty) { assert(0) ; } } if (innerIdentifiers(dec->u.var.var)) { assert(0) ; } S_enter(venv , dec->u.var.var ,E_VarEntry(tmp.ty)) ; return; } } assert(0) ; } Ty_ty transTy(S_table tenv , A_ty ty) { switch(ty->kind) { case A_nameTy : { if (S_Symbol("int") == ty->u.name) { return Ty_Int(); } if (S_Symbol("string") == ty->u.name) { return Ty_String(); } Ty_ty tmp = (Ty_ty)S_look(tenv , ty->u.name) ; if ( tmp == NULL ) { assert(0) ; } return Ty_Name(ty->u.name , tmp) ; } case A_recordTy : { A_fieldList tmpfeldList = ty->u.record ; Ty_fieldList tyfdlist = NULL ; while(tmpfeldList) { Ty_ty tmp = (Ty_ty)S_look(tenv , tmpfeldList->head->typ) ; if ( tmp == NULL ) { assert(0) ; } if (innerIdentifiers(tmpfeldList->head->name)) { assert(0); } tyfdlist = Ty_FieldList(Ty_Field( tmpfeldList->head->name , tmp ) , tyfdlist) ; tmpfeldList = tmpfeldList->tail ; } return Ty_Record(tyfdlist); } case A_arrayTy : { Ty_ty tmp = (Ty_ty)S_look(tenv , ty->u.array); if ( tmp == NULL ) { assert(0); } return Ty_Array(tmp) ; } } assert(0) ; } bool innerIdentifiers( S_symbol sym) { if (sym == S_Symbol("int") || sym == S_Symbol("string") ) { return true ; } return false ; }
函式 S_changeBind 相關程式碼
S_symbol.cpp void* S_changeBind(S_table t , S_symbol sym , void *value) { return TAB_changeBind(t , sym , value) ; } S_table.cpp void* TAB_changeBind( TAB_table t , void * key , void *value ) { int index ; assert(t&&key) ; binder b ; void * tmp ; index = ((unsigned)key) % TABSIZE ; for ( b = t->table[index] ; b ; b = b->next) { if (b->key == key ) { tmp = b->value ; b->value = value ; return tmp ; } } return NULL ; }