深刻V8引擎-AST(4)

(再聲明一下,爲了簡單暴力的講解AST的轉換過程,這裏的編譯內容以"'Hello' + ' World'"做爲案例)git

上一篇基本上花了一整篇講完了scanner的Init方法,接下來就是Scan了,Init的方法基本上都是在Stream類下操做,可是本節回到了scanner層級。es6

/**
 * Scan
 * 僅僅只涉及next_指針
 */
void Scanner::Scan() { Scan(next_); }
void Scanner::Scan(TokenDesc* next_desc) {
  next_desc->token = ScanSingleToken();
  /**
   * 設置當前詞法的結束位置
   */
  next_desc->location.end_pos = source_pos();
}

雖然這裏只有簡簡單單的兩步(砍掉了全部的CHECK和DEBUG內容),但這個ScanSingleToken已經夠講了。從字面意思理解,就是對單個詞法的解析,源碼以下。數組

/**
 * 這個ScanSingleToken方法可TM太長了
 */
V8_INLINE Token::Value Scanner::ScanSingleToken() {
  Token::Value token;
  do {
    /**
     * 設置當前詞法的起始位置
     */
    next().location.beg_pos = source_pos();
    /**
     * Ascii碼是從0 ~ 127
     * 簡單的判斷一下合法性
     */
    if (V8_LIKELY(static_cast<unsigned>(c0_) <= kMaxAscii)) {
      /**
       * 這是一個mapping數組
       * 對全部的Unicode => Ascii作了映射
       */
      token = one_char_tokens[c0_];
      /**
       * 包含很是多的case...先不展開了
       * 根據Token類型進行不一樣的處理
       */
      switch (token) {
        case Token::LPAREN:
        case Token::RPAREN:
        // 其餘單符號...
          // One character tokens.
          return Select(token);
        case Token::STRING:
          return ScanString();

        // 更多...
        default:
          UNREACHABLE();
      }
    }
    /**
     * 處理結束符、空格、異常符號等特殊狀況
     */
    // ...
  } while (token == Token::WHITESPACE);

  return token;
}

做爲一個詞法解析方法,長度其實仍是能夠接受的,已經刪掉了大部分的case判斷,因爲本系列專一於"'Hello' + ' World'"的編譯,因此留下了STRING類型。app

講兩個點,第一個是那個source_pos,位置的屬性和方法是真的多,比較簡單,看看就好了。編碼

/**
 * 上一篇解析了第一個字符 因此pos移動到了1
 * 然而記錄location須要從頭開始 因此這裏作了一個偏移
 */
static const int kCharacterLookaheadBufferSize = 1;
int source_pos() {
  return static_cast<int>(source_->pos()) - kCharacterLookaheadBufferSize;
}

而後那個mapping數組能夠稍微給一下出處,源碼以下。spa

/**
 * 總結起來就是GetOneCharToken(0),GetOneCharToken(1),...,GetOneCharToken(127)所有調用一遍
 * 其中IsDecimalDigit負責判斷是不是數字
 * 而IsAsciiIdentifier負責判斷是不是標識符,例如$、_、a-z等等
 * 最後生成的one_char_tokens數組下標表明Unicode編碼 值表明對應的Token類型
 */

#define INT_0_TO_127_LIST(V)                                          \
V(0)   V(1)   V(2)   V(3)   V(4)   V(5)   V(6)   V(7)   V(8)   V(9)   \
// ...
V(120) V(121) V(122) V(123) V(124) V(125) V(126) V(127)

static const constexpr Token::Value one_char_tokens[128] = {
#define CALL_GET_SCAN_FLAGS(N) GetOneCharToken(N),
    INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS)
#undef CALL_GET_SCAN_FLAGS
};

constexpr Token::Value GetOneCharToken(char c) {
  // clang-format off
  return
    c == '(' ? Token::LPAREN :
    c == ')' ? Token::RPAREN :
    // 其他字符...
    IsDecimalDigit(c) ? Token::NUMBER :
    IsAsciiIdentifier(c) ? Token::IDENTIFIER :
    Token::ILLEGAL;
}

以前說過,c0_表明的是當前解析字符的Unicode編碼,因而這裏直接經過數組索引查找其對應的類型,按照例子中,咱們的字符是一個單引號,而單引號的類型以下。指針

/**
 * 單雙引號均會被識別爲字符串標記
 * 而es6的模板字符串比較特殊 暫時不搞他
 */
c == '"' ? Token::STRING :
c == '\'' ? Token::STRING :
c == '`' ? Token::TEMPLATE_SPAN :

因此,當前token被賦值爲Token::STRING,所以,case分支進入ScanString的方法。這個方法內容比較多,下一篇講吧,午休時間。code

相關文章
相關標籤/搜索