2011-04-29 51 views
3

我有我想象中的一個相當複雜的技術挑戰:我希望能夠以多種語言可靠地進行字母重命名標識符(儘可能多)。這需要對每種語言進行特殊考慮,並且我正在徵求如何最小化我需要通過共享代碼完成的工作量的建議。像一個已經支持多種語言的統一解析或抽象語法框架就好了。Alpha以多種語言重命名

例如,這裏是一些Python代碼:

def foo(x): 
    def bar(y): 
     return x+y 
    return bar 

x阿爾法重新命名y改變xy保留語義。因此,這將成爲:

def foo(y): 
    def bar(y1): 
     return y+y1 
    return bar 

看看我們如何需要的,以便從破譯密碼保持重命名yy1?這就是爲什麼這是一個難題。看起來程序必須對構成範圍的內容有很好的瞭解,而不僅僅是做一個字符串搜索和替換。

我也想保留儘可能多的格式:評論,間距,縮進。但這不是100%必要的,它會很好。

任何提示?

+1

所以基本上你正在尋找一個完整的多語言解析器? – 2011-04-29 20:15:32

+0

這是可能的。我只需要關於名稱的內涵以及它們所指的內容的信息。該語言的其他語義是無關緊要的。但是AFAICT確實需要解析。 – luqui 2011-04-29 20:47:05

+0

單獨的解析器甚至不會這樣做 - 他必須模擬每個標識符的範圍解析,這對於某些語言來說意味着將匿名導入的範圍考慮在內。這聽起來像是一個幾乎不可能完成的任務,如果不支持什麼語言的限制。 – 2011-04-29 20:47:08

回答

-1

無論背景如何,語言大多都會保證令牌是唯一的。一個天真的第一種方法(這將打破很多代碼,許多作品)將是:

cp file file.orig 
sed -i 's/\b(newTokenName)\b/TEMPTOKEN/g' file 
sed -i 's/\b(oldTokenName)\b/newTokenName/g' file 

隨着GNU sed的,這將在PHP打破。將\ b重寫爲一般符號匹配,如([^ a-zA-Z〜$ -_] [^ a-zA-Z0-9〜$ -_])可用於大多數C,Java,PHP和Python ,但不包括Perl(需要在令牌字符中添加@和%,除此之外,它需要一個適用於任何你想添加的語言的插件架構,在某些情況下,會有兩種語言的變量和函數命名規則將不兼容,在這一點上,您需要在插件中做更多更多的事情。

+0

是的,我開始認爲這可能是最簡單的。即使它是愚蠢的,並且它有可能在字符串內部進行替換(嗯,並且函數或變量名稱出現在字符串中的可能性很高,因爲函數會「關於」那個東西)。詞法分析器可能比完整的解析器更易於理解,然後這種技術可以工作。 – luqui 2011-04-29 21:04:03

+0

我添加了複製行。此時,可以很容易地將其包裝到shell腳本中 - 包含具有標記的文件(對grep使用相同的規則)以及diff文件file.orig。在實際使用中,這將是快速使用和程序員控制之間的良好組合。理論上講,「完美」解決方案需要使用GCC的大部分前端。 – 2011-04-29 21:22:41

+0

@luqui:是的,這是最簡單的。與幾乎所有「最簡單」的解決方案一樣,它不是一個好的解決方案。 – 2011-04-29 22:11:54

1

您可以嘗試爲涉及的語言創建基於Xtext的實現方案Xtext框架爲跨語言重命名重構提供了可靠的基礎結構但是,您必須爲每種語言至少提供一個「足夠好」的範圍分辨率的語法。

+0

有趣,看起來很有希望。我在尋找框架,因爲希望人們已經爲自己喜歡的語言編寫了語法,所以這是我的希望。我希望我不必跑日本鬼怪。我會研究它,謝謝你的鏈接。 – luqui 2011-04-29 21:50:06

+0

Xtext是一個基於Eclipse的框架,因此您將確定運行Eclipse UI。目前不支持通用語言實現的語法動物園。 – 2011-04-29 21:53:41

5

要做是安全的,你需要能夠確定

  • 所有標識符(和那些沒有的東西,例如,註釋的中間)在你的代碼
  • 有效性的範圍爲每IDENTIFER
  • 替換一個新的標識符的舊文本
  • ,以確定是否重命名一個標識符導致另一名能力的能力被隱藏

爲了精確地確定標識符,您需要至少一個語言準確的詞法分析器。 PHP中的標識符與COBOL中的標識符不同。

要確定有效性的範圍,您必須在實踐中確定程序結構,因爲大多數「範圍」是由此類結構定義的。這意味着你需要一個語言準確的解析器; PHP中的作用域與COBOL中的作用域不同。

要確定哪些名稱在哪些範圍內有效,您需要知道語言範圍規則。您的語言可能會堅持認爲,標識符X將根據發現X的上下文引用不同的Xes(考慮具有不同參數的名爲X的對象構造函數)。現在您需要能夠根據命名規則遍歷範圍結構。單繼承,多繼承,重載,默認類型幾乎都需要爲程序構建一個範圍模型,將標識符和相應的類型插入到每個範圍中,然後從遇到的標識符根據語言語義通過各種範圍編程文本。您將需要符號表,繼承鏈接,AST和導航所有這些的能力。這些結構與PHP和COBOL不同,但它們有許多共同的想法,所以您可能需要一個具有共同概念支持的庫。

重命名一個標識符,您必須修改文本。在一百萬行代碼中,您需要仔細點。修改AST節點是仔細指出的一種方法。實際上,您需要修改全部與正在重命名的標識符對應的標識符;你必須爬到樹上才能找到它們,或者在AST中記錄所有參考文獻,以便可以很容易地找到它們。修改樹後,必須在修改AST後重新生成源文本。這是很多機器;請參閱我的SO answer on how to prettyprint ASTs預先保存您合理建議應保留的所有內容。 (您的其他選擇是跟蹤字符串的文本在哪裏, 以及讀取/修補/寫入文件。)

在更新文件之前,您需要檢查您是否存在沒有什麼影子。考慮以下代碼:

{ local x; 
    x=1; 
    {local y; 
    y=2; 
     {local z; 
     z=y 
     print(x); 
     } 
    } 
} 

我們同意此代碼打印「1」。現在我們決定將y重命名爲x。 我們已經打破了範圍界定,現在從概念上將 引用到外部x的打印語句指的是由重命名的y捕獲的x。代碼現在打印「2」,所以我們的重命名打破了它。這意味着必須檢查可能找到重命名變量的範圍中的所有其他標識符,以查看新名稱是否「捕獲」了我們不期望的某個名稱。 (如果打印語句打印出來,這將是合法的)。

這是很多機器。

是的,有一個框架,幾乎所有這些以及一些強大的語言前端。請參閱我們的DMS Software Reengineering Toolkit。它具有生成AST的解析器,從AST生成文本的漂亮打印機,通用符號表管理機制(包括支持多繼承),AST訪問/修改機制。它擁有可以將AST轉化爲文本的機器。它具有front ends for C, C++, COBOL and Java,它們實現名稱和類型解析(例如,將符號表範圍和標識符即時符號表項映射);它的許多其他語言的前端尚未實施範圍界定。

我們剛剛完成了爲Java實現「重命名」的練習。 (當然所有上述問題都出現了)。我們即將開始爲C++開發一個。