2017-07-17 76 views
2

編輯:對於那些有興趣/誰想看到我在做什麼,我的應用程序的源代碼可以找到hereANTLR:如何避免用戶修改文本時重新解析整個文件


我正在用C#構建一個代碼編輯器應用程序,它提供語法高亮顯示。我目前使用ANTLR for C#來解析代碼以突出顯示它。到目前爲止,當用戶最初打開文件時,我的應用程序可以非常快速地突出顯示代碼。不過,當用戶開始編輯文本時,我沒有編寫任何代碼來重新突出顯示文本。

我希望編輯器能夠很好地處理大文件,所以我不希望每次用戶輸入一個字符時都要重新解析整個文件。我做了一些研究,看起來我正在尋找的是一個增量式解析器。不幸的是,它看起來像ANTLR v4 can't do incremental parsing,所以我不確定要做什麼。

我的問題是:是否有另一種方法可以使用ANTLR在用戶輸入時不凍結應用程序?我真的很難放棄ANTLR,因爲它有a bunch of free grammars可用,所以增加對新語言的支持沒有太多工作。我查看了TextMate語法,VSCode使用了很多,但我不理解它們,也沒有可用的C#庫來操作它們。

感謝您的幫助!

+0

你確定你甚至需要解析文件的語法高亮嗎?通常,語法高亮所需的只是令牌流,它不僅計算速度更快,而且僅重新標記文件的一小部分也更容易。如果您想使用語義信息,如果您想突出顯示實例變量而不是局部變量,則這是不夠的。但是,如果你想要的只是一種顏色的標識符,另一種是這組關鍵字等,基於令牌的突出顯示更容易和更高性能。 – sepp2k

+0

您是否知道[Roslyn](https://github.com/dotnet/roslyn/wiki/Roslyn%20Overview)? –

+0

@ sepp2k你是對的,如果我想要所有的標識符都是相同的顏色,我可以標記源代碼,但代碼看起來幾乎是單調的。例如,我想爲不同於本地變量標識符的顏色類型命名。 –

回答

2

我不會在每次按鍵後解析,但我會解析整個文件。這適用於我創建的特定於域的語言中的中等大小文件。而不是試圖解析該文件的唯一部分,我用的是混合的方法,解析在第一任的三個條件存在:

  1. 用戶類型ň字符
  2. 計時器已表示,沒有變化在m毫秒。
  3. 對於某些語法,用戶輸入行結束符/分隔符;

底線是,您可能會驚訝於人們花費多少時間來暫停和思考,因爲他們正在輸入任何對他們施加語法的東西。這些暫停可以被用來在用戶認爲有用的時候進行工作,甚至可以達到400毫秒。由於語法的原因,我在創建的DSL中使用#1和#2。

的「沒有變化」鍾到達每個按鍵事件後復位,當解析後ň字符,則會出現當然ñ字符計數器被設定。我發現像這樣的組合方法在IDE類型的環境中運行良好。

需要記住的一件事是,如果你這樣做,在找到語法錯​​誤時不要亂用文本控件的插入點,因爲錯誤在鍵入時不可避免。我只是在標籤中顯示一條消息:

public override void Recover(Parser recognizer, RecognitionException e) 
    { 
     IToken token = recognizer.CurrentToken; 
     string message = string.Format("parse error at line {0}, position {1} right before {2} ", token.Line, token.Column, GetTokenErrorDisplay(token)); 
     BasicEnvironment.SyntaxError = message; 

在我的使用環境中,計時器通常在關閉時進行管理;具有800毫秒的值和10個字符,我獲得了很好的結果,通常在解析開始時定時器控制。

+0

JLH,非常感謝您的回答。我認爲我會在這個問題上陷入僵局,但是你讓我看到有增量解析的替代方案。一個問題 - 你對n的價值是什麼?也是米= 400? –

+0

@JamesKo我用當前值編輯了我的答案。必須檢查以刷新我的記憶。 ;)希望這對於「接受」乾杯來說是足夠的。 – TomServo

+1

JLH,我會在幾天內接受你的回答。由於這是一個廣泛的問題,我不想阻止其他人回答。不過,我非常感謝你的回答,這就是爲什麼我已經向你投票贊成。 –

1

@JLH基本上說明了我使用的方法,但我想添加一些您可能需要注意的事項:
首先我會在不同的線程中進行解析,您的編輯器,以防止解析器在用戶決定繼續編碼的同時啓動的情況。如果您使用的是不同的線程,則用戶可以繼續編碼,而不會注意到解析器在後臺運行。當然你需要使用某種機制來取消在這種情況下的解析,或者至少在編譯器中沒有用生成的分析樹做任何事情,因爲它已經是obsolte了。

接下來的事情是我發現ANTLR在中等大小的文件(〜100行)上會變得非常慢。而且非常慢,我的意思是解析可能需要20秒!這可以通過將ANTLR的默認解析算法切換爲SLL並使用BailoutStrategy來避免,從而避免從語法錯誤中恢復。如果發生這種情況,您必須使用正常的LL模式再次解析整個事件,以檢查它是否確實是語法錯誤或僅僅是SLL無法處理的內容。
通過這種方法,我將一些文件中的解析時間從〜20s縮短到〜1s。

你可以看看我的source code(雖然它是在java中,但原則應該是相同的)工作(肯定不完美)實現這一點。

+0

Raven:感謝關於切換解析算法的額外信息,我對這個問題一無所知(我還沒有用含有語法錯誤的文件進行測試)。我有一個問題 - 我可以使用什麼機制來「取消解析」? .NET有一個'CancellationToken'類型,它允許您在完成之前取消異步方法,我認爲這就是我們在這裏尋找的。但是,由於ANTLR是用Java編寫的(而ANTLR for C#鏡像原始的API),我不認爲解析器支持那樣的取消。你有沒有想出這樣的機制? –

+0

不,我沒有找到取消ANTLR解析器的方法。我所做的是在解析線程中設置一個標誌,在ANTLR完成解析之後,我檢查該標誌,如果它是真的,我不處理parseTree。這不是最好的解決方案,但它可以完成這項工作...... – Raven

+0

@JamesKo也許你應該研究一下救助策略或者一個不容錯誤的自定義詞法分析器,並且在第一個詞中停止。在SO上搜索或詢問具體問題。我將這些技術用於特定領域的語言,以避免在最小的錯誤發生後繼續進行lexing/parsing。 – TomServo