• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆-341  評論-2670  文章-0  trackbacks-0

                詞法分析器生成器終于做好了,因此我又畫了一個狀態(tài)機然后生成了一個詞法分析器,因此開始研究IDE的智能提示的技術(shù)了。智能提示的技術(shù)有幾個要點,第一個是無論怎么慢都不能妨礙你打字,第二個是崩潰了也不能讓IDE關(guān)掉,要重啟分析器。因此我做了一個小實驗。首先我將NativeX語言的著色器跟詞法分析器都做好了,因此我要做的事情就是在你打字的時候,用另外一個線程進(jìn)行詞法分析,得到結(jié)果之后用一個框框來展示出光標(biāo)所在的那個token:




                注意光標(biāo)所在的位置是在方框的內(nèi)部。為了實現(xiàn)這個過程,首先我們要一個安全的多線程機制。我們把詞法分析作為一個請求來看,在打字的過程中我們就不斷把請求發(fā)送到詞法分析器頭上,然后詞法分析器分析完了就會發(fā)個消息給窗口然后傳送結(jié)果。我們注意到如果打字太快導(dǎo)致來不及處理的話,可能會積累若干個請求,不過那些請求實際上只有最后一個需要被處理,之前的那些來不及處理的舊請求都會被忽略。反正我們的字已經(jīng)打完了,詞法分析器就不需要分析那些我們還沒打完的字了。因此我們把它抽象一下:

                我們有一個請求的類,這個類需要的數(shù)據(jù)有輸入類型、輸出類型、計算方法以及傳送方法。為什么要傳送方法呢?因為計算是在另外一個線程完成的,因此在調(diào)用傳送方法的時候也是在另外一個線程完成的,所以我們得提供一個函數(shù)來接受分析后的結(jié)果,然后用一種安全的方法傳送到我們想要的地方去。因此我們就把這個過程分成了兩個類,一個處理多線程的問題的抽象類,和一個用來提供計算方法和傳送方法的子類:
             1 using System;
             2 using System.Collections.Generic;
             3 using System.Linq;
             4 using System.Text;
             5 using System.Threading;
             6 
             7 namespace CodeBoxControl
             8 {
             9     public abstract class CalculationNotifier<I, O> : IDisposable
            10     {
            11         private Thread workingThread = null;
            12         private I analyzingInput = default(I);
            13         private bool analyzingInputAvailable = false;
            14         private Semaphore codeSemaphore = null;
            15         private object locker = null;
            16 
            17         public CalculationNotifier()
            18         {
            19             this.locker = new object();
            20             this.codeSemaphore = new Semaphore(01);
            21             this.workingThread = new Thread(Run);
            22             this.workingThread.Start();
            23         }
            24 
            25         public void Analyze(I input)
            26         {
            27             bool needRelease = false;
            28             lock (this.locker)
            29             {
            30                 if (!this.analyzingInputAvailable)
            31                 {
            32                     needRelease = true;
            33                 }
            34                 this.analyzingInputAvailable = true;
            35                 this.analyzingInput = input;
            36             }
            37             if (needRelease)
            38             {
            39                 this.codeSemaphore.Release(1);
            40             }
            41         }
            42 
            43         public void Dispose()
            44         {
            45             this.workingThread.Abort();
            46             this.codeSemaphore.Dispose();
            47         }
            48 
            49         protected abstract O Calculate(I input);
            50         protected abstract void Receive(O output);
            51 
            52         private void Run()
            53         {
            54             while (true)
            55             {
            56                 this.codeSemaphore.WaitOne();
            57                 I input = default(I);
            58                 lock (this.locker)
            59                 {
            60                     input = this.analyzingInput;
            61                     this.analyzingInputAvailable = false;
            62                 }
            63                 O output = Calculate(input);
            64                 Receive(output);
            65             }
            66         }
            67     }
            68 }
            69 

                怎么使用它呢?我們只需要不斷地把數(shù)據(jù)發(fā)送給Analyze函數(shù),那個函數(shù)就會自動替我們處理好跟同步相關(guān)的所有問題了,然后在另外一個線程調(diào)用分析函數(shù)和返回結(jié)果:
             1 using System;
             2 using System.Collections.Generic;
             3 using System.Linq;
             4 using System.Text;
             5 using CodeBoxControl;
             6 using CodeBoxControl.CodeProvider;
             7 
             8 namespace CodeForm
             9 {
            10     class NativeXAnalyzingResult
            11     {
            12         public List<CodeToken> Tokens { getset; }
            13     }
            14 
            15     interface INativeXAnalyzingResultReceiver
            16     {
            17         void Receive(NativeXAnalyzingResult result);
            18     }
            19 
            20     class NativeXCodeAnalyzer : CalculationNotifier<string, NativeXAnalyzingResult>
            21     {
            22         private NativeXTokenizer tokenizer = new NativeXTokenizer();
            23         private INativeXAnalyzingResultReceiver receiver = null;
            24 
            25         public NativeXCodeAnalyzer(INativeXAnalyzingResultReceiver receiver)
            26         {
            27             this.receiver = receiver;
            28         }
            29 
            30         protected override NativeXAnalyzingResult Calculate(string input)
            31         {
            32             NativeXAnalyzingResult result = new NativeXAnalyzingResult();
            33             result.Tokens = this.tokenizer.Tokenize(input.ToCharArray());
            34             return result;
            35         }
            36 
            37         protected override void Receive(NativeXAnalyzingResult output)
            38         {
            39             this.receiver.Receive(output);
            40         }
            41     }
            42 }
            43 

                這個類就是用來負(fù)責(zé)做詞法分析的了。當(dāng)然我們注意到此時接受數(shù)據(jù)的過程還是被抽象掉了,因為這個過程實際上應(yīng)該讓插件去處理,因為插件才知道要怎么畫框。目前的控件支持兩個插件,一個是著色器,另一個是監(jiān)視器。監(jiān)視器負(fù)責(zé)監(jiān)聽字符串的改變和插手繪圖的過程,我們可以看出這個插件是如何把詞法分析結(jié)果和畫框框聯(lián)系在一起的:
              1 using System;
              2 using System.Collections.Generic;
              3 using System.Linq;
              4 using System.Text;
              5 using CodeBoxControl;
              6 using System.Windows.Forms;
              7 using CodeBoxControl.Core;
              8 using CodeBoxControl.CodeProvider;
              9 using System.Drawing;
             10 
             11 namespace CodeForm
             12 {
             13     class NativeXControlPanel
             14         : ITextEditorControlPanel
             15         , INativeXAnalyzingResultReceiver
             16         , IDisposable
             17     {
             18         private ITextEditorControlPanelCallBack callback = null;
             19         private NativeXAnalyzingResult analyzingResult = null;
             20         private NativeXCodeAnalyzer analyzer = null;
             21 
             22         private TextPosition grayStart = new TextPosition(00);
             23         private TextPosition grayEnd = new TextPosition(00);
             24 
             25         public int Width
             26         {
             27             get
             28             {
             29                 return 0;
             30             }
             31         }
             32 
             33         public void InstallCallBack(ITextEditorControlPanelCallBack callback)
             34         {
             35             this.analyzer = new NativeXCodeAnalyzer(this);
             36             this.callback = callback;
             37             this.callback.TextEditorBox.SelectionChanged += new EventHandler(TextEditorBox_SelectionChanged);
             38         }
             39 
             40         public void OnEdit(CodeBoxControl.Core.TextPosition start, CodeBoxControl.Core.TextPosition oldEnd, CodeBoxControl.Core.TextPosition newEnd)
             41         {
             42             this.analyzer.Analyze(this.callback.TextEditorBox.Text);
             43         }
             44 
             45         public bool NeedColorLineForDisplay(int lineIndex)
             46         {
             47             return this.grayStart != this.grayEnd && this.grayStart.row <= lineIndex && lineIndex <= this.grayEnd.row;
             48         }
             49 
             50         public void ColorLineForDisplay(int lineIndex, int[] colors)
             51         {
             52             TextLine<TextEditorBox.LineInfo> line = this.callback.TextEditorBox.TextProvider[lineIndex];
             53             int length = line.CharCount;
             54 
             55             int start = grayStart.row == lineIndex ? Math.Min(grayStart.col, length) : 0;
             56             int end = grayEnd.row == lineIndex ? Math.Min(grayEnd.col, length) : line.CharCount;
             57             for (int i = start; i < end; i++)
             58             {
             59                 colors[i] = NativeXColorizer.BlockPointColorId;
             60             }
             61         }
             62 
             63         public void DrawLineBackground(Graphics g, int lineIndex, Rectangle backgroundArea)
             64         {
             65         }
             66 
             67         public void DrawLineForeground(Graphics g, int lineIndex, Rectangle backgroundArea)
             68         {
             69             if (NeedColorLineForDisplay(lineIndex))
             70             {
             71                 TextLine<TextEditorBox.LineInfo> line = this.callback.TextEditorBox.TextProvider[lineIndex];
             72                 int length = line.CharCount;
             73                 int start = grayStart.row == lineIndex ? Math.Min(grayStart.col, length) : 0;
             74                 int end = grayEnd.row == lineIndex ? Math.Min(grayEnd.col, length) : line.CharCount;
             75 
             76                 int x1 = this.callback.TextEditorBox.TextPositionToViewPoint(new TextPosition(lineIndex, start)).X;
             77                 int x2 = this.callback.TextEditorBox.TextPositionToViewPoint(new TextPosition(lineIndex, end)).X;
             78                 g.DrawRectangle(Pens.Gray, x1, backgroundArea.Top, x2 - x1, backgroundArea.Height);
             79             }
             80         }
             81 
             82         public void DrawControlPanel(Graphics g, int lineIndex, Rectangle controlPanelArea)
             83         {
             84         }
             85 
             86         public void DrawControlPanelBackground(Graphics g, Rectangle backgroundArea)
             87         {
             88         }
             89 
             90         public void OnMouseDown(int lineIndex, Rectangle controlPanelArea, Point relativePosition, System.Windows.Forms.MouseButtons buttons)
             91         {
             92         }
             93 
             94         public void OnMouseMove(int lineIndex, Rectangle controlPanelArea, Point relativePosition, System.Windows.Forms.MouseButtons buttons)
             95         {
             96         }
             97 
             98         public void OnMouseUp(int lineIndex, Rectangle controlPanelArea, Point relativePosition, System.Windows.Forms.MouseButtons buttons)
             99         {
            100         }
            101 
            102         public void Receive(NativeXAnalyzingResult result)
            103         {
            104             this.callback.TextEditorBox.Invoke(new MethodInvoker(() =>
            105             {
            106                 this.analyzingResult = result;
            107                 UpdateBlock();
            108             }));
            109         }
            110 
            111         public void Dispose()
            112         {
            113             this.analyzer.Dispose();
            114         }
            115 
            116         private void UpdateBlock()
            117         {
            118             NativeXAnalyzingResult result = this.analyzingResult;
            119             TextPosition pos = this.callback.TextEditorBox.SelectionCaret;
            120             if (result != null)
            121             {
            122                 foreach (CodeToken token in result.Tokens)
            123                 {
            124                     if (token.Start < pos && pos < token.End)
            125                     {
            126                         this.grayStart = token.Start;
            127                         this.grayEnd = token.End;
            128                         return;
            129                     }
            130                 }
            131                 this.grayStart = new TextPosition(00);
            132                 this.grayEnd = new TextPosition(00);
            133             }
            134         }
            135 
            136         private void TextEditorBox_SelectionChanged(object sender, EventArgs e)
            137         {
            138             UpdateBlock();
            139         }
            140     }
            141 }
            142 


                這里我們借助了System.Windows.Forms.Control.Invoke來將一整個閉包壓到了消息循環(huán)里面,然后消息循環(huán)來執(zhí)行這個閉包,因此接收數(shù)據(jù)的過程就在UI線程上完成了。Invoke跟SendMessage其實是差不多的。我們還能看出這個插件的接口叫ControlPanel,所以是用來干預(yù)一切變化的。這里有Width屬性但是設(shè)置成0了,如果不是0的話就可以模仿VisualStudio左邊那個放斷點的控制欄了,這個在之前已經(jīng)演示過了。

                到這里如何在非UI線程處理代碼的技術(shù)就介紹完了,現(xiàn)在讓我們來看著色器和詞法分析器的樣子哈。首先上著色器的狀態(tài)機:


                然后是詞法分析器的狀態(tài)機:


                至于他們生成的代碼,可以去Vczh Library++ 3.0這里看哈。

             

            posted on 2010-10-14 08:23 陳梓瀚(vczh) 閱讀(5861) 評論(6)  編輯 收藏 引用 所屬分類: 開發(fā)自己的IDE

            評論:
            # re: 開發(fā)自己的IDE(六)[未登錄] 2010-10-14 08:33 | Lyt
            看一次流一次口水…  回復(fù)  更多評論
              
            # re: 開發(fā)自己的IDE(六) 2010-10-14 16:51 | Pear
            MB真帥氣。。MB真帥氣。。真帥氣。。  回復(fù)  更多評論
              
            # re: 開發(fā)自己的IDE(六) 2010-10-16 07:57 | mm
            好強大!!!  回復(fù)  更多評論
              
            # re: 開發(fā)自己的IDE(六) 2010-10-17 08:45 | hhh2000
            圖有點小,有些符號看不清楚……

            對著色器狀態(tài)機的圖有點疑問:1、如果CharIn是指單個字符的話,好像少了一個狀態(tài)。2、StringIn指向自己的邊是非 \ 和 " 號嗎(圖看不清)?這好像不大對吧。還

            有單雙引號一般只影響本行,加上一個行尾的狀態(tài)約束一下,這樣在引號不匹配的情況下看起來會好一些。
              回復(fù)  更多評論
              
            # re: 開發(fā)自己的IDE(六) 2010-10-19 01:41 | 陳梓瀚(vczh)
            @hhh2000
            為了簡單,char我還是允許他擁有多個字符了,反正對于一個字符的狀態(tài)可以正確解析,而且這是用在IDE上面的,勢必跟編譯器會有一些小的區(qū)別

            StringIn那個東西是\.哈,也就是任意字符的意思。  回復(fù)  更多評論
              
            # re: 開發(fā)自己的IDE(六) 2010-10-19 01:42 | 陳梓瀚(vczh)
            @hhh2000
            到了行末尾其實就是一個\n字符,只要你在狀態(tài)機上面把\n給排除了,自然就會只有一行了。  回復(fù)  更多評論
              
            日本精品久久久中文字幕| 国产成人综合久久久久久| 久久精品天天中文字幕人妻| 99久久综合狠狠综合久久止| 精品久久久久久久久久中文字幕 | 无码精品久久一区二区三区| 狠狠色狠狠色综合久久| 99久久精品国产一区二区蜜芽| 伊人情人综合成人久久网小说| 日本福利片国产午夜久久| 久久久久久久波多野结衣高潮 | 国产成人精品久久亚洲| 久久久国产精华液| 久久国产乱子伦精品免费午夜| 人妻无码αv中文字幕久久琪琪布| 伊人久久综在合线亚洲2019 | 久久精品国内一区二区三区| 亚洲精品99久久久久中文字幕 | 国产成人精品久久一区二区三区av | 欧洲人妻丰满av无码久久不卡| 午夜福利91久久福利| 69久久夜色精品国产69| 国产精品美女久久福利网站| 国产精品成人99久久久久91gav| 久久精品中文騷妇女内射| 99久久国产宗和精品1上映| 亚洲国产精品无码久久九九| 久久精品国产亚洲5555| 国产亚州精品女人久久久久久 | 国内精品久久久久久中文字幕| 久久国产欧美日韩精品| 久久夜色精品国产噜噜亚洲AV| 99久久综合国产精品免费| 色婷婷久久久SWAG精品| 久久国产福利免费| 婷婷久久综合九色综合九七| 麻豆久久| 久久久久久久波多野结衣高潮| 日韩欧美亚洲综合久久| 久久久久99这里有精品10| 久久伊人五月丁香狠狠色|