1. 程式人生 > >lcc 原始碼讀書筆記之c語言的語義檢測

lcc 原始碼讀書筆記之c語言的語義檢測

    LCC在語法分析過程中進行了不少的語義檢測,基本集中在一元及二元操作符這裡.C語言的語義檢查主要包括隱式轉換,型別檢測和計算順序。那就讓我們從LCC編譯器的角度研究C語言的語義規則

 轉換:

   c語言在進行二元計算的時候常常包括返回型別及運算元的型別轉換,下面貼出常規算術轉換的程式碼

Type binary(Type ty1,Type ty2)
{
   if(isdouble(ty1)||isdouble(ty2))
     return doubletype;
   if(ty1==floattype||ty2==floattype)
    return floattype;
  if(isunsigned(ty1)||isunsinged(ty2))
    return unsignedtype;
  return inttype;
}

根據上述程式碼可以看出來字元型別的算術操作都是返回INT型別

在處理例如char* p for(p)這種語句時。LCC要將這種不是布林表示式的式子轉換為布林表示式,程式碼如下

Tree cond(Tree p)
{
   int op=gernic(rightkid(p)->op);
 
   if(op==AND||op==布林操作符..)
    return p;
   p=pointer(p);
   p=cast(p,promote(p->type));  //這個是將P轉換為具有目標型別的樹,用CV操作符
   return (*optree[NEQ])(NE,p,constree(0,inttype));
}

運算子&處理型別為T的運算元,並返回他的地址(型別為(POINTER T))。

if(isarray(p->type)||isfunc(p->type))
  return retype(p,ptr(p->type));           //陣列或者函式,返回指向陣列元素或者函式的指標
else
  p=lvalue(p);                                  //返回指向本型別的指標,既左值
 if(isaddrop(p->op)&&p->u.sym->sclass==REGISTER)
  error();
 else if(isaddrop(p->op))
   p->u.sym->addressed=1;               //取址運算的運算元不能只保留在暫存器中

運算子*的語義檢測如下

p=pointer(p);
if (isptr(p->type)&&isfunc(p->type->type))
   return retype(p,p->type->type);           //函式的取值型別就是函式原來的型別
else{
  p=rvalue(p);         } //一般指標的取值操作符是INDIR,型別為所指型別

為了方便大家更好的理解LCC各個模組的協作,從軟體工程的角度看這個系統,我貼出一張UML順序圖

 

  從軟體工程的角度上看,LCC有高內聚,低耦合的特點。各個模組比較易於理解,可重用性也強,而且建立類的職責分配的也不錯,有點疑問的是VISITOR直接由解析器建立好了,還是引入一個抽象類。從這圖上看C跟C++的差役也沒那麼大,重構成C++的看起來還是很容易的

   扯遠了,呵呵。下面看看c語言的[ ]操作。標準C規定e[i]等價於*(e+i);語義函式如下:

  Tree q;
 t=gettok();
 q=expr(]);
 p=(*optree['+'])(ADD,pointer(p),pointer(q));
 if(isptr(p->type)&&isarray(p->type->type)
  p=retype(p,p->type->type)
 else
  return p=rvalue(p); //取右運算元

其中執行加法操作的關鍵處為:

   if(isptr(r->type)&&isint(l->type)          
   &&!isfunc(r->type->type)
        {
                int n;
                ty=unqual(r->type);                 
                n=type->size;                  //取出指標所指型別的大小       
                if(n==0)
                   error();
                l=cast(l,promote(l->type));        //短整數型別要提升
                if(n>1)
                  l=multree(MUL,consttree(n,inttype),1);        //要加的數位指標所指型別的大小×n
                return simplify(ADD+P,ty,l,r);    //返回的型別仍是指標

      }  

 下面再看看->操作符的語義檢測,相關的語義檢測動作主要包含在field(Tree p,char* name)函式中,放在設計模式中,相當於是一個語義檢測Visitor

Tree field(Tree p,char *name)
{
   Field q;
   Type ty1,ty=p->type;
   ty1=ty;
   ty=unqual(ty);
   if((q=fieldref(name,ty)))!=Null){               //保證是結構的域,否則出錯
    if(!isarray(q->type)){
      ty=q->type;
      ty=ptr(ty);
    }
    p=simply(ADD+P,ty,p,consttree(q->offset,inttype));     //加上偏移量
    if (q->lsb) {
   p = tree(FIELD, ty->type, rvalue(p), NULL);
   p->u.field = q;
               }        else if (!isarray(q->type))
  p = rvalue(p);}                    //返回右值
    else
       error("unknow field");
}

c語言的語義檢測暫時介紹到這兒,關於函式呼叫,常量摺疊等比較複雜,下一次繼續,呵呵