官术网_书友最值得收藏!

2.4.3 標(biāo)識符、數(shù)字、字符和字符串的詳細(xì)分析過程

1.標(biāo)識符的詳細(xì)分析過程

先來看lex_identifier函數(shù),即標(biāo)識符的識別過程,情景如圖2-51所示。

圖2-51 標(biāo)識符的狀態(tài)轉(zhuǎn)換圖

代碼如下所示:

//代碼路徑:libcpp/lex.c:
 static cpp_hashnode *
 lex_identifier (cpp_reader *pfile, const uchar *base, bool starts_ucn, struct normalize_state *nst)
 {
   ……
   cur = pfile->buffer->cur;                        // cur指向的是下一個(gè)字符
   if (! starts_ucn)
     while (ISIDNUM (*cur))                        // 一直檢測,若下一個(gè)字符不是字母、數(shù)字或下劃線,則跳出循環(huán)
       {
          hash = HT_HASHSTEP (hash, *cur);
          cur++;                                        // 符合字母、數(shù)字或下劃線條件,cur就繼續(xù)指向下一個(gè)字符
       }
   pfile->buffer->cur = cur;                        // 跳出循環(huán)后,進(jìn)入終態(tài)
   ……
}
// 代碼路徑:include/safe-ctype.h:
  ……
 _sch_isidnum  = _sch_isidst|_sch_isdigit, /* A-Za-z0-9_ */
 ……
 #define ISIDNUM(c) _sch_test(c, _sch_isidnum)        // 字母、數(shù)字或下劃線的判斷條件展開形式
 ……
}
2.數(shù)字的詳細(xì)分析過程

下面我們來看數(shù)字的識別過程。后續(xù)的數(shù)字識別過程在狀態(tài)轉(zhuǎn)換圖的基礎(chǔ)上做了調(diào)整,大體分為兩步:第一步是,只要識別出是數(shù)字,就把后續(xù)內(nèi)容當(dāng)作字符串存起來,不管是什么;第二步是,根據(jù)字符串中的內(nèi)容,分析它的具體屬性,狀態(tài)轉(zhuǎn)換圖的思想主要體現(xiàn)在第二步。

我們先來看第一步,通過調(diào)用lex_number函數(shù)來實(shí)現(xiàn)。代碼如下所示:

//代碼路徑:libcpp/lex.c:
 static void
 lex_number (cpp_reader *pfile, cpp_string *number, struct normalize_state *nst)
 {
     ……
     cur = pfile->buffer->cur;
       /* N.B. ISIDNUM does not include $.  */
       while (ISIDNUM (*cur) || *cur == '.' || VALID_SIGN (*cur, cur[-1]))
                                                                // 只要符合數(shù)字規(guī)則,就繼續(xù)循環(huán)
       {
         cur++;                                                // cur不斷地指向后面的ASCII碼,直到遇到空格,跳出循環(huán)
         NORMALIZE_STATE_UPDATE_IDNUM (nst);
       }
       pfile->buffer->cur = cur;                                // 跳出循環(huán)后,buffer->cur指向空格
     }
   ……
   number->len = cur - base;                                // 計(jì)算出數(shù)字有多長
   dest = _cpp_unaligned_alloc (pfile, number->len + 1);  
                                                                // 為存儲數(shù)字開辟空間
   memcpy (dest, base, number->len);                        // 把數(shù)字的內(nèi)容記錄下來
   dest[number->len] = '\0';                                // 最后加上“\0”,表明是以字符串的形式記錄下
   number->text = dest;
}
// 代碼路徑:include/safe-ctype.h:
 ……
 _sch_isidnum  = _sch_isidst|_sch_isdigit, /* A-Za-z0-9_ */
 ……
 #define ISIDNUM(c) _sch_test(c, _sch_isidnum)                // 字母、數(shù)字或下劃線的判斷條件展開形式
 ……
}
// 代碼路徑:libcpp/internal.h:
 ……
 /* Test if a sign is valid within a preprocessing number.  */
 #define VALID_SIGN(c, prevc) \
   (((c) == '+' || (c) == '-') && \
    ((prevc) == 'e' || (prevc) == 'E' \
     || (((prevc) == 'p' || (prevc) == 'P') \
        && CPP_OPTION (pfile, extended_numbers))))
 #define CPP_OPTION(PFILE, OPTION) ((PFILE)->opts.OPTION)        // 科學(xué)計(jì)數(shù)法的識別條件
 ……

我們再來看第二步。前面介紹了詞法分析的函數(shù)調(diào)用順序。

第二步等cpp_get_token_with_location函數(shù)返回后,在c_lex_with_flags函數(shù)中進(jìn)行。代碼如下所示:

//代碼路徑:gcc/c-family/c-lex.c:
 enum cpp_ttype
 c_lex_with_flags (tree *value, location_t *loc, unsigned char *cpp_flags, int lex_flags)
 {
     ……
    tok = cpp_get_token_with_location (parse_in, loc);
    type = tok->type;
 retry_after_at:
   switch (type)
     {
         ……
         unsigned int flags = cpp_classify_number (parse_in, tok, &suffix, *loc);
                                                                // 此函數(shù)將確定數(shù)據(jù)的具體屬性
         ……
     }
    ……
}

數(shù)字的屬性包括三種:類型屬性(整型、浮點(diǎn)型等)、進(jìn)制屬性(十進(jìn)制、八進(jìn)制、十六進(jìn)制等)和后綴信息(如短類型、寬類型等輔助類型信息),下面我們來介紹cpp_classify_number函數(shù)。情景如圖2-52至圖2-56所示。

圖2-52 十進(jìn)制數(shù)的狀態(tài)轉(zhuǎn)換圖

圖2-53 八進(jìn)制數(shù)的狀態(tài)轉(zhuǎn)換圖

圖2-54 十六進(jìn)制數(shù)的狀態(tài)轉(zhuǎn)換圖

圖2-55 十進(jìn)制數(shù)浮點(diǎn)數(shù)的狀態(tài)轉(zhuǎn)換圖

圖2-56 十六進(jìn)制數(shù)浮點(diǎn)數(shù)的狀態(tài)轉(zhuǎn)換圖

cpp_classify_number函數(shù)的代碼如下所示:

//代碼路徑:libcpp/expr.c:
 unsigned int
 cpp_classify_number (cpp_reader *pfile, const cpp_token *token,
              const char **ud_suffix, source_location virtual_location)
 {
     const uchar *str = token->val.str.text;        // 獲取字符串的內(nèi)容(前面lex_number函數(shù)中已經(jīng)保存了內(nèi)容)
     const uchar *limit;
     unsigned int max_digit, result, radix;
     enum {NOT_FLOAT = 0, AFTER_POINT, AFTER_EXPON} float_flag;
                                                        // 關(guān)于浮點(diǎn)數(shù)的三種標(biāo)志:NOT_FLOAT為非浮點(diǎn)標(biāo)志,
                                                        // AFTER_POINT為已經(jīng)識別到浮點(diǎn)的標(biāo)志,
                                                        // AFTER_EXPON為科學(xué)計(jì)數(shù)法標(biāo)志(exponent:指數(shù))
     bool seen_digit;
    ……
    if (token->val.str.len == 1)                        // 如果字符串的長度為1
       return CPP_N_INTEGER | CPP_N_SMALL | CPP_N_DECIMAL;
                                                        // 那就不用再分析了,這個(gè)數(shù)據(jù)就是個(gè)十進(jìn)制的短整型
    limit = str + token->val.str.len;                // 通過起始字符和字符串長度值,獲取字符串末尾字符
    float_flag = NOT_FLOAT;                        // 先默認(rèn)將要分析的數(shù)字是非浮點(diǎn)數(shù)
    max_digit = 0;                                // 默認(rèn)識別到的最大數(shù)字是0
    radix = 10;                                        // 先默認(rèn)將要分析的數(shù)字是十進(jìn)制數(shù)
    seen_digit = false;
    // 先確定數(shù)字是個(gè)幾進(jìn)制的數(shù)
    if (*str == '0')                                // 識別到第一個(gè)字符是“0
    {
       radix = 8;                                        // 就肯定不是十進(jìn)制數(shù)了,先默認(rèn)是八進(jìn)制數(shù),往后看看再說
       str++;                                        // 準(zhǔn)備往后遍歷
       /* Require at least one hex digit to classify it as hex.  */
       if ((*str == 'x' || *str == 'X')                // 如果第二個(gè)字符是“x”或“X”,同時(shí)第三個(gè)字符
                                                        // 是“.”或0~9A~Fa~f
       && (str[1] == '.' || ISXDIGIT (str[1])))
       {
         radix = 16;                                // 說明是十六進(jìn)制數(shù)
         str++;
       }
        else if ((*str == 'b' || *str == 'B') && (str[1] == '0' || str[1] == '1'))
      {                                                // 如果第二個(gè)字符是“b”或“B”,同時(shí)第三個(gè)字符
                                                        // 是“0”或“1
         radix = 2;                                // 說明是二進(jìn)制數(shù)
         str++;
      }
     }
     // 到這里就確定了數(shù)據(jù)是幾進(jìn)制數(shù)
    // 循環(huán)中判斷數(shù)字是非浮點(diǎn)數(shù)還是浮點(diǎn)數(shù),以及有沒有使用科學(xué)計(jì)數(shù)法
    for (;;)
    {
       unsigned int c = *str++;
      if (ISDIGIT (c) || (ISXDIGIT (c) && radix == 16))
                                                        // 識別到0~9或者十六進(jìn)制下的0~9A~Fa~f
     {
        seen_digit = true;                        // 確定當(dāng)前識別到的字符是數(shù)字
        c = hex_value (c);
        if (c > max_digit)                        // 不斷更改識別到的最大數(shù)字
          max_digit = c;
     }
    else if (c == '.')                                // 識別到浮點(diǎn)了
    {
      if (float_flag == NOT_FLOAT)                // 如果此時(shí)還默認(rèn)數(shù)字是非浮點(diǎn)數(shù)
        float_flag = AFTER_POINT;                // 就要改設(shè)為浮點(diǎn)數(shù)了,而且確定此時(shí)已經(jīng)遍歷過了浮點(diǎn)
        ……
    }
      else if ((radix <= 10 && (c == 'e' || c == 'E'))
           || (radix == 16 && (c == 'p' || c == 'P')))
                                                        // 識別到了科學(xué)計(jì)數(shù)法標(biāo)志
     {
       float_flag = AFTER_EXPON;                        // 設(shè)置科學(xué)計(jì)數(shù)法標(biāo)志
       break;                                        // 終止對科學(xué)計(jì)數(shù)法部分?jǐn)?shù)據(jù)的進(jìn)一步識別,跳出循環(huán)
     }
     else
     {
        /* Start of suffix.  */
        str--;
        break;                                        // 如果執(zhí)行到這里,整個(gè)數(shù)字的數(shù)值內(nèi)容就算識別完了,
                                                        // 要么是非浮點(diǎn)數(shù),要么是浮點(diǎn)數(shù),但不會(huì)有科學(xué)計(jì)數(shù)法,
                                                        // 識別完后,準(zhǔn)備識別后綴信息
     }
    }
    if (radix != 16 && float_flag == NOT_FLOAT)        // 確定數(shù)字不是十六進(jìn)制數(shù),而且是非浮點(diǎn)數(shù)
     {
      result = interpret_float_suffix (pfile, str, limit - str);
                                                        // 設(shè)置后綴信息
      ……
      else
      result = 0;                                        // 后綴信息設(shè)置為0,這個(gè)result最終將用來存儲
                                                        // 數(shù)字的全部屬性信息
     }
     if (float_flag != NOT_FLOAT && radix == 8)        // 如果數(shù)字是浮點(diǎn)型或包含科學(xué)計(jì)數(shù)法,同時(shí)還是個(gè)八進(jìn)制數(shù)
       radix = 10;                                // 設(shè)置為十進(jìn)制數(shù)
    ……
    if (float_flag != NOT_FLOAT)                        // 數(shù)字是浮點(diǎn)型或包含科學(xué)計(jì)數(shù)法
    {
        ……
      if (float_flag == AFTER_EXPON)                // 如果用了科學(xué)計(jì)數(shù)法,前面跳出了循環(huán),這里繼續(xù)遍歷
     {
        if (*str == '+' || *str == '-')                // 判斷指數(shù)是“+”還是“-
          str++;
        ……
        do
          str++;
        while (ISDIGIT (*str));                        // 只要是0~9,就說明符合指數(shù)規(guī)則,繼續(xù)遍歷
     }
     result = interpret_float_suffix (pfile, str, limit - str);
                                                        // 設(shè)置后綴信息
     ……
     result |= CPP_N_FLOATING;                        // 確定此數(shù)字為浮點(diǎn)數(shù),設(shè)置標(biāo)志位
 }
 else                                                // 數(shù)字是非浮點(diǎn)數(shù)
 {
   result = interpret_int_suffix (pfile, str, limit - str);
                                                        // 設(shè)置后綴信息
   ……
   result |= CPP_N_INTEGER;                        // 確定此數(shù)字為非浮點(diǎn)數(shù),設(shè)置標(biāo)志位
 }
 ……
 // 到這里為止,后綴類型信息都已經(jīng)確定了,下面最后再把幾進(jìn)制這一信息加上,數(shù)字就算分析完了
 if (radix == 10)
     result |= CPP_N_DECIMAL;                        // 確定數(shù)字為十進(jìn)制數(shù),設(shè)置信息
   else if (radix == 16)
     result |= CPP_N_HEX;                                // 確定數(shù)字為十六進(jìn)制數(shù),設(shè)置信息
   else if (radix == 2)
     result |= CPP_N_BINARY;                        // 確定數(shù)字為二進(jìn)制數(shù),設(shè)置信息
   else
     result |= CPP_N_OCTAL;                        // 確定數(shù)字為八進(jìn)制數(shù),設(shè)置信息
   return result;                                        // 數(shù)字分析完畢,返回結(jié)果
 ……
}
3.字符或字符串的詳細(xì)分析過程

先來看lex_string函數(shù),即字符或字符串的識別過程,情景如圖2-57和圖2-58所示。

圖2-57 字符常量的狀態(tài)轉(zhuǎn)換圖一

圖2-58 字符常量的狀態(tài)轉(zhuǎn)換圖二

代碼如下所示:

//代碼路徑:libcpp/lex.c:
static void
lex_string (cpp_reader *pfile, cpp_token *token, const uchar *base)
{
  ……
  cur = base;
  terminator = *cur++;                                // 獲取到字符或字符串的第一個(gè)字符,cur指向第二個(gè)字符
  if (terminator == 'L' || terminator == 'U')        // 先看看字符或字符串前面有沒有修飾符,“L”、“U”、“u”、“8
                                                        // 都是修飾符
     terminator = *cur++;
  else if (terminator == 'u')
  {
     terminator = *cur++;
     if (terminator == '8')
       terminator = *cur++;
  }
  ……
  // 開始識別字符或字符串的實(shí)質(zhì)內(nèi)容了
  if (terminator == '"')                                // 識別到“"”,說明是字符串
    type = (*base == 'L' ? CPP_WSTRING :        // 根據(jù)修飾符有無或種類來設(shè)置字符串的類型
       *base == 'U' ? CPP_STRING32 :
       *base == 'u' ? (base[1] == '8' ? CPP_UTF8STRING : CPP_STRING16) : CPP_STRING);
  else if (terminator == '\'')                        // 識別到“\'”,說明是字符
    type = (*base == 'L' ? CPP_WCHAR :                // 根據(jù)修飾符有無或種類來設(shè)置字符的類型
       *base == 'U' ? CPP_CHAR32 :
       *base == 'u' ? CPP_CHAR16 : CPP_CHAR);
  else
    terminator = '>', type = CPP_HEADER_NAME;        // 還有一種情況,此時(shí)正在識別某個(gè)頭文件的名字,設(shè)置類型
for (;;)                                                // 繼續(xù)向后遍歷字符或字符串的內(nèi)容
   {
       cppchar_t c = *cur++;                        // 根據(jù)字符串或字符的詞法規(guī)定, "”或“\”成對出現(xiàn)
       ……
       else if (c == terminator)                        // 找到另一個(gè)“"”或“\”,說明內(nèi)容識別完畢了
         break;
       else if (c == '\n')                        // 遇到“\n”,就要準(zhǔn)備處理頭文件的名字了
     {
        cur--;                                        // cur指針往前退一個(gè)字符
        if (terminator == '>')                        // 退了一個(gè)正好是“>”,說明頭文件的名字識別完畢
        {
          token->type = CPP_LESS;
          return;
        }
        ……
     }
       ……
    }……
}
主站蜘蛛池模板: 灌南县| 普定县| 蒲城县| 肇庆市| 慈利县| 克什克腾旗| 额尔古纳市| 松溪县| 福鼎市| 南宁市| 改则县| 凤庆县| 西和县| 彭阳县| 武清区| 峨眉山市| 民丰县| 南汇区| 会同县| 大厂| 顺平县| 大名县| 广河县| 宜宾市| 西乌珠穆沁旗| 田林县| 孝感市| 新昌县| 阳曲县| 鹤庆县| 玛沁县| 内黄县| 芜湖市| 黔西县| 武川县| 石首市| 三明市| 花莲县| 锡林郭勒盟| 新田县| 开远市|