本文章是由機器翻譯。

程式設計師雜談

Roslyn 的興起

Joe Hummel
Ted Neward

Ted Neward幾年來,各種電腦專業、 思想領導和專家有主張特定于域的語言 (Dsl) 作為一種方式接近解決方案的軟體問題。這似乎是特別合適,如果 DSL 語法是"普通使用者"可以用來調整和修改商務規則在一個系統中的東西。這是許多開發人員對軟體的聖杯 — — 建設制度的人能保持自己的業務需要更改時。

DSL 的主要批評之一然而,是這一事實寫一個編譯器是"失傳的藝術"。它並非罕見來自各行各業的程式師來看待創建編譯器或解譯器作為一種黑暗的藝術。

生成 2014年會議今年,微軟正式宣佈在 Microsoft.NET 框架開發生態系統中最壞的不斷的秘密之一 — — 開源的羅斯林。這是基礎的 C# 和Visual Basic的語言修改重新生成編譯器系統。對於一些人來說,這是微軟將其語文放入開源社區並獲得好處的機會 — — bug 修復、 增強功能,新的語言功能進行公開檢討等等。對於開發人員來說,這是機會更深入地看一下如何編譯器 (和口譯員 — — 雖然羅斯林集中在彙編給出的語言所) 在引擎蓋下開展工作。

為更多的背景 (和安裝提示) 查閱在羅斯林 CodePlex 頁 roslyn.codeplex.com。由於總是與尚未釋放的位,這強烈建議你做這一個虛擬機器或電腦上你不關心太多。

羅斯林基本面

在較高水準,編譯器的目標是翻譯成可執行檔的輸出,如.NET 程式集或本機.exe 檔的程式師輸入 (原始程式碼)。雖然編譯器中的模組的確切名稱各不相同,我們通常認為一個編譯器為劃分成兩個基本部分:前端和後端 (見圖 1)。

高級編譯器的設計
圖 1 高級編譯器的設計

前端的關鍵職責之一是驗證入站的原始程式碼中的格式設置精度。與所有程式設計語言一樣,有是以特定的格式,程式師必須遵循保持清晰和明確到機器的事情。例如,請考慮下面的 C# 語句:

if x < 0   <-- syntax error!
  x = 0;

這是語法不正確,因為如果條件必須包圍,就像這樣:

if (x < 0)
  x = 0;

一旦解析代碼,是後端負責源如型別安全侵犯的更深層次驗證:

string x = "123";
if (x < 0)                   <-- semantic error!
  x = 0;                     <-- semantic error!

順便說一下,這些例子是由語言實施者的深思熟慮的設計決定。他們受到長時間的辯論哪些是比別人的"好"。聽到更多,訪問任何線上程式設計論壇,請在中,鍵入"D00d 你語言差。"你很快就會發現自己捲入"教育"的會議,這肯定是一個要記住。

假設沒有句法或語意錯誤,編譯將繼續和後端翻譯等效程式在目的語言中所需的投入。

更深層次的深處

雖然你可能需要用最簡單的語言的兩部分方法,最常語言編譯器翻譯是分解成得更多。在下一級的複雜性,大多數編譯器安排自己在六個主要階段,在前端的兩個和四個後端採取行動 (見圖 2)。

編譯器的主要階段
圖 2 編譯器的主要階段

前端執行前兩個階段:詞法分析和語法分析。詞法分析的目標是要讀取輸入的程式並輸出的標記 — — 關鍵字、 識別碼、 標點、 等等。此外保持每個標記的位置,所以程式的格式不會丟失。假設下面的程式片段從原始檔案的開頭:

// Comment
if (score>100)
  grade = "A++";

詞法分析的輸出將此序列的權杖:

IfKeyword                       @ Span=[12..14)
OpenParenToken              @ Span=[15..16)
IdentifierToken                  @ Span=[16..21), Value=score
GreaterThanToken             @ Span=[21..22)
NumericLiteralToken           @ Span=[22..25), Value=100
CloseParenToken              @ Span=[25..26)
IdentifierToken                  @ Span=[30..35), Value=grade
EqualsToken                    @ Span=[36..37)
StringLiteralToken             @ Span=[38..43), Value=A++
SemicolonToken               @ Span=[43..44)

每個權杖進行的其他資訊,例如開始和結束位置 (Span) 從原始檔案的開始處計算。通知 IfKeyword 從 12 位置開始。 這是由於橫跨的評論 [0..10) 和行尾字元,跨度 [10..12)。雖然從技術上講不標記,從詞法分析器的輸出通常包括空白,包括注釋有關的資訊。在.NET 編譯器中,轉達了作為語法瑣事的空白。

編譯器的第二階段解析。分析器工作手與詞法分析器來執行語法分析。解析器沒有工作,其中絕大多數從詞法分析器請求權杖,因為它會檢查對源語的各種語法規則的輸入的程式。例如,C# 程式師都知道語法的一個 if 語句:

if  (  condition  )  then-part  [ else-part ]

[......] 象徵 else 部分是可選的。解析器強制實施此規則匹配的標記,並運用更複雜的語法元素,如條件和那部分的附加規則:

void if( )
{
  match(IfKeyword);
  match(OpenParenToken);
  condition();
  match(CloseParenToken);
  then_part();
  if (lookahead(ElseKeyword))
  else_part();
}

函數 match(T) 調用詞法分析器來獲取下一個標記,並檢查是否此權杖匹配 t。 編譯會正常繼續它是否匹配。否則,它會報告一個語法錯誤。最簡單的解析器使用 match 函數引發異常時的語法錯誤。這有效地將暫停編譯。這是一種這類的實現:

void match(SyntaxToken T)
{
  var next = lexer.NextToken();
  if (next == T)
  ;  // Keep going, all is well:
  else
  throw new SyntaxError(...);
}

幸運的是,.NET 編譯器包含一個更為複雜的解析器。它是能夠繼續面臨嚴重的語法錯誤。

假設有沒有語法錯誤,前端是本質上完成的。它有但餘下的一項任務 — — 來傳達其努力到後端。表單中,這存儲在內部被稱為它的中間表示形式 — — 或駕駛執照。 (儘管在術語中的相似性,紅外光譜具有與.NET 通用中間語言無關。.NET 編譯器中的分析器生成作為紅外光譜、 抽象語法樹 (AST),並將此樹傳遞給後端。

樹木是天然的紅外,鑒於 C# 和Visual Basic程式的層次結構。程式將包含一個或多個類。類包含屬性和方法、 屬性和方法包含語句、 語句通常包含塊和塊包含附加的語句。AST 的目標是代表基於其句法結構的程式。"抽象派"在 AST 中表示語法糖的缺乏如 ; 和 ()。例如,請考慮下面的 C# (假設這些編譯沒有錯誤) 的語句序列:

sum = 0;
foreach (var x in A)   // A is an array:
  sum += x;
avg = sum / A.Length;

在較高水準,為此代碼片段 AST 會看起來像圖 3

高級別抽象語法樹的 C# 代碼片段 (細節丟失為簡單起見)
圖 3 高級別抽象語法樹的 C# 代碼片段 (細節丟失為簡單起見)

AST 捕獲關於該程式的必要資訊:聲明,聲明、 破碎的每個語句等等的順序。不必要的語法是被拋棄,如所有分號。關鍵的功能,以瞭解在 AST 圖 3 是它捕獲程式的句法結構。

換句話說,它是編寫的程式是如何,沒有怎麼它 exe­益。考慮 foreach 語句,迴圈零或多次作為它迴圈遍歷一個集合。AST 捕獲 foreach 語句的組成部分 — — 迴圈變數、 集合和身體。AST 不傳達的就是 foreach 可能重複幾遍。事實上,如果你看看這棵樹,有是沒有箭頭在樹中表示 foreach 執行的方式。知道的唯一方法是通過瞭解 foreach 關鍵字 = = 迴圈。

Ast 是非常好的紅外,與一個主要優點:他們容易生成和理解。缺點是更複雜的分析,比如那些用在編譯器的後端是更難在 AST 上執行。出於此原因,編譯器通常維護多個美國國稅局,包括常見的替代方法到 AST。此替代方法是控制流圖 (CFG) 表示一個基於其控制流的程式:迴圈,如果那時 else 語句、 異常等等。(我們將在下一篇專欄文章多。)

學習 AST 中.NET 編譯器如何使用的最佳方法是通過羅斯林語法視覺化檢視。這是作為 Roslyn SDK 的一部分安裝的。一旦安裝完畢,在Visual Studio2013年中打開任何 C# 或Visual Basic程式,請將游標放在源程式碼中感興趣的和打開視覺化檢視。你會看到視圖功能表中,其他視窗和羅斯林語法視覺化檢視 (請參閱圖 4)。

在Visual Studio2013 Roslyn 語法視覺化檢視
圖 4 在Visual Studio2013 Roslyn 語法視覺化檢視

作為一個具體的例子,考慮如果我們解析早些時候的聲明:

 

// Comment
  if (score>100)
    grade = "A++";

圖 5 顯示了相應的 AST 片段由.NET 編譯器。

為 IfStatement.NET 編譯器所建的抽象語法樹
圖 5 為 IfStatement.NET 編譯器所建的抽象語法樹

因為有很多的事情,這棵樹似乎壓倒性在第一次。但是記住兩件事。之一,這棵樹是簡單地擴大早些時候的源語句,所以它實際上很容易穿過樹林,看看它如何映射回原始的源。而兩個,AST 用於機消費,不為人。通常唯一的一次人類看 AST 是調試一個解析器。要牢記,以及更完整的課程上進行詞法分析和語法分析是我們這兒的空間的範圍。有很多的資源提供給那些想要深入瞭解這項工作。這裡的目標是一個溫柔的介紹,不深潛。

總結

我們還沒有完成與羅斯林任何伸展的想像,敬請關注。你感興趣 Roslyn 潛水深入它,我們可以建議安裝羅斯林。然後看一看一些文檔,從羅斯林 CodePlex 頁開始。

如果你想要更深入解析和詞法分析,有很多書可用。還有令人尊敬的"龍書"也被稱為"編譯器:原則、 技術 & 工具"(艾迪生 Wesley,2006年)。如果你感興趣的更多。以網路為中心方法,考慮"編譯的.NET 公共語言運行時 (CLR)"由John高夫 (普倫蒂斯霍爾,2001年) 或夏麥"寫編譯器和 Interpeters:一種軟體工程方法"(威利,2009 年)。您編碼愉快 !


Joe Hummel是在芝加哥,伊利諾伊大學的內容建立者為研究副教授 Pluralsight.com,Visual c + + MVP 和一名私人顧問。 他獲得博士學位 加州大學歐文分校的高性能計算領域是平行的所有東西感興趣。他居住在芝加哥地區,和當他並不帆船可以到達在 joe@joehummel.net

Ted Neward 是 iTrellis,一家諮詢服務公司的 CTO。他已寫超過 100 篇文章,寫了十幾本書,包括"專業 F # 2.0" (Wrox,2010年)。他是 F # MVP,在世界各地的會議上講話。他提供諮詢和指導定期 — — 達到他在 ted@tedneward.comted@itrellis.com 如果你有興趣。

感謝以下的微軟技術專家對本文的審閱:DustinCampbell