先聲明一下,這種長系列的大塊頭博客只能保證儘量的深刻到每一行源碼,有些代碼我不樂意深究就寫個註釋說明一下做用。另外,因爲本地整理的比較好,博客就隨心寫了。ui
整個Compile過程目前只看到asmjs以前,簡單的過了幾遍,大部分方法沒有點進去看,實在是太複雜了。上一篇的結尾指出了AST的入口,也就是命名空間parsing的一個公共方法,以下。編碼
bool ParseProgram(ParseInfo* info, Isolate* isolate) { // ... /** * 生成一個Parser實例 * 調用內部方法啓動轉換 */ Parser parser(info); FunctionLiteral* result = nullptr; /** * 轉換AST後將結果賦值給ParseInfo的literal_ */ result = parser.ParseProgram(isolate, info); info->set_literal(result); // ... return (result != nullptr); }
所須要關心的核心代碼就是這些,很是簡單,Parser對象的初始化屬性很是多,這裏就不列出來了。spa
接下來進入第二個核心方法,即ParseProgram。code
FunctionLiteral* Parser::ParseProgram(Isolate* isolate, ParseInfo* info) { // ... /** * scanner_爲Parser類的一個私有屬性 * 這裏僅僅進行初始化 */ scanner_.Initialize(); FunctionLiteral* result = DoParseProgram(isolate, info); // ... return result; }
一樣,所須要關心代碼只有兩行,其中第一步則是啓動了scanner的初始化,第二步則是開始全面解析。對象
Scanner包含scanner、scanner-character-strams兩個部分,其中stream則是通過初步處理的源String,必須轉換後才能進行解析。處理的過程在以前省略的代碼中,這裏稍微給出大概的轉換流程。blog
bool ParseProgram(ParseInfo* info, Isolate* isolate) { // ... /** * 一、info->script()返回的是字符串的描述信息 source是Local<String>類型的源字符串 * 二、ScannerStream是scanner-character-streams頭文件的類 內部方法均爲靜態類型 能夠直接調用 * 三、返回的具體類型根據String類型不一樣而不一樣 可是因爲均繼承於Utf16CharacterStream 因此直接用父類接 */ Handle<String> source(String::cast(info->script()->source()), isolate); std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For(isolate, source)); info->set_character_stream(std::move(stream)); // ... } /** * 有四種特殊的String類型 分別new不一樣的子類 * ScannerStream::For(isolate, data, 0, data->length()); */ Utf16CharacterStream* ScannerStream::For(Isolate* isolate, Handle<String> data, int start_pos, int end_pos) { size_t start_offset = 0; // ... if (data->IsSeqOneByteString()) { return new BufferedCharacterStream<OnHeapStream>( static_cast<size_t>(start_pos), Handle<SeqOneByteString>::cast(data), start_offset, static_cast<size_t>(end_pos)); } }
常規的字符串通常都是OneByteString,這裏就不細講了。最後返回一個特殊Stream類,其屬性記錄字符串的長度、當前的解析進度、解析的開始與結束標記等等。繼承
將字符串轉換後,就能夠利用Scanner來進行逐步解析,在此以前,須要對Scanner類有一個簡單的瞭解,以下。token
/** * Scanner類 * 跟Utf16CharacterStream一個文件 */ class V8_EXPORT_PRIVATE Scanner { public: // 返回next_的token類型 Token::Value peek() const { return next().token; } // 返回current_的位置信息 const Location& location() const { return current().location; } private: // 當前字符的Unicode編碼 -1表示結尾(typedef int32_t uc32) uc32 c0_; TokenDesc* current_; // desc for current token (as returned by Next()) TokenDesc* next_; // desc for next token (one token look-ahead) TokenDesc* next_next_; // desc for the token after next (after PeakAhead()) // 從Handle<String>轉換後的類型 負責執行解析的實際類 Utf16CharacterStream* const source_; }
選取了一些比較簡單的屬性和方法,Scanner內部有三個遊標屬性負責遍歷字符串,分別是current_、next_、next_next_,字面意思理解就好了。source_則是以前說的轉換Stream類,全部的解析實際上都是調用這個屬性的方法。而兩個結構體TokenDesc、Location也很是重要,一個負責詞法描述,一個負責記錄詞法位置信息,以下。ip
/** * 詞法結構體 * 每個TokenDesc表明單獨一段詞法 */ struct TokenDesc { /** * 詞法所在位置 * 該結構體比較簡單 就不展開了 兩個值表明起始、結束位置 * 例如sample中 "'Hello' + ' World'" 'Hello'會被解析爲TOKEN::STRING location爲{0, 7} */ Location location = {0, 0}; /** * 字符串詞法相關 */ LiteralBuffer literal_chars; LiteralBuffer raw_literal_chars; /** * 詞法的枚舉類型 * 例如 '(' 是 TOKEN::LPAREN '===' 是 TOKEN::EQ_STRICT * 全部類型可見token.h */ Token::Value token = Token::UNINITIALIZED; MessageTemplate invalid_template_escape_message = MessageTemplate::kNone; Location invalid_template_escape_location; // 小整數 uint32_t smi_value_ = 0; bool after_line_terminator = false; }
經過這個結構體和一些方法,就能完整的將源字符串逐步轉換爲抽象語法樹。可是實際轉換過程很是複雜,分支極多,後面再繼續探究。字符串