﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-λ-calculus（长门流的程序员们，2010年12月18日，这是胜利的光芒在指引我们前进！）-随笔分类-开发自己的IDE</title><link>http://www.cppblog.com/vczh/category/14937.html</link><description>【QQ：343056143（已满），504254078】【MSN：vczh@hotmail.com】【Email：vczh@163.com】</description><language>zh-cn</language><lastBuildDate>Thu, 24 Feb 2011 17:02:50 GMT</lastBuildDate><pubDate>Thu, 24 Feb 2011 17:02:50 GMT</pubDate><ttl>60</ttl><item><title>写程序真他妈爽啊</title><link>http://www.cppblog.com/vczh/archive/2011/02/25/140618.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Thu, 24 Feb 2011 16:54:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2011/02/25/140618.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/140618.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2011/02/25/140618.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/140618.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/140618.html</trackback:ping><description><![CDATA[花了两天时间，集成了以前开发的山寨Office2007的Ribbon框架，和之前开发的Intellisense算法和控件，打算做一个可以点击Run之后出一只小乌龟画图的小demo哇哈哈。然后以此作为<a style="TEXT-DECORATION: underline" href="http://vlpp.codeplex.com/" target=_blank>Vczh Library++ 3.0</a>的第一个Release的Demo。<br><br>vle.exe大概已经做完了，现在checkin了一个vlscript.dll的框架，但是还没有实现里面的具体的函数。到时候这个用C#写的demo就会调用C++写的vlscript.dll，来实现乌龟画图功能。<br><br>这个破IDE的代码在&#8220;Tools\Release\VleSource\VlTurtle\VlTurtle.csproj&#8221;，大家有兴趣的话可以去下载。打开那个solution，然后用release编译，然后再release目录下执行Deploy.bat之后，就可以F5看到效果了。<br><br>此时的心情简直无以伦比。<br><br><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/vczh/Turtle_Sample_01.jpg" width=751 height=600> 
<img src ="http://www.cppblog.com/vczh/aggbug/140618.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2011-02-25 00:54 <a href="http://www.cppblog.com/vczh/archive/2011/02/25/140618.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发自己的IDE（十二）</title><link>http://www.cppblog.com/vczh/archive/2010/12/05/135505.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Sun, 05 Dec 2010 02:59:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2010/12/05/135505.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/135505.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2010/12/05/135505.html#Feedback</comments><slash:comments>16</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/135505.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/135505.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 时隔一个月，终于在<a style="FONT-SIZE: 12pt; TEXT-DECORATION: underline" href="http://vlpp.codeplex.com/" target=_blank><strong style="BACKGROUND-COLOR: yellow; FONT-SIZE: 14pt">Vczh Library++ 3.0</strong></a>的IDE工程里实现了用户自定义类型变绿（抄C#的娃哈哈）鼠标移动到对象上会出tooltip、显示函数参数提示和Code snippet的功能了。由于我的<a style="TEXT-DECORATION: underline" href="http://www.cppblog.com/vczh/archive/2010/11/22/134302.html" target=_blank>语法树</a>的每一个节点都包含了它在源代码中的位置，所以鼠标指向一个字符的时候，可以计算出他在语法树的位置，从而可以查到他的声明的语法树节点，再用声明节点的位置就可以把声明的代码复制出来显示在tooltip上面了：<br>
<div align=center src_cetemp="/images/cppblog_com/vczh/2010_Intellisense_NativeX_21.jpg"><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/vczh/2010_Intellisense_NativeX_21.jpg" width=436 height=112></div>
<br>&nbsp;&nbsp;&nbsp; 同样的方法可以用来实现显示函数参数。这个比tooltip要复杂一点，因为要吧语法树的声明重新组织成代码才能显示出来，而且还有模板函数的类型问题。不过现在有两个小功能还没实现，第一个是在没输入的时候移动光标不会更新参数，这只是个小问题。另一个是对一个函数指针调用的时候做提示，这个还要进一步考虑。下图显示的是对一个模板函数做参数提示的时候，参数的类型会自动特化：<br>
<div align=center src_cetemp="/images/cppblog_com/vczh/2010_Intellisense_NativeX_22.jpg"><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/vczh/2010_Intellisense_NativeX_22.jpg" width=375 height=104></div>
<br>&nbsp;&nbsp;&nbsp; 接下来就是code snippet了。Code snippet是一个强大的功能，可以让你免除一些无谓的手指的劳动。Visual Studio里面提供了大量的code snippet，不过在这里我暂时只提供for和forr两种，用来输入for循环。下面是使用code snippet输入一个for循环的全过程：<br>
<div align=center src_cetemp="/images/cppblog_com/vczh/2010_Intellisense_NativeX_23.jpg"><img border=0 src="http://www.cppblog.com/images/cppblog_com/vczh/2010_Intellisense_NativeX_23.jpg"></div>
<br><br>
<div align=center src_cetemp="/images/cppblog_com/vczh/2010_Intellisense_NativeX_24.jpg"><img border=0 src="http://www.cppblog.com/images/cppblog_com/vczh/2010_Intellisense_NativeX_24.jpg"></div>
<br><br>
<div align=center src_cetemp="/images/cppblog_com/vczh/2010_Intellisense_NativeX_25.jpg"><img border=0 src="http://www.cppblog.com/images/cppblog_com/vczh/2010_Intellisense_NativeX_25.jpg"></div>
<br><br>
<div align=center src_cetemp="/images/cppblog_com/vczh/2010_Intellisense_NativeX_26.jpg"><img border=0 src="http://www.cppblog.com/images/cppblog_com/vczh/2010_Intellisense_NativeX_26.jpg"></div>
<br><br>
<div align=center src_cetemp="/images/cppblog_com/vczh/2010_Intellisense_NativeX_27.jpg"><img border=0 src="http://www.cppblog.com/images/cppblog_com/vczh/2010_Intellisense_NativeX_27.jpg"></div>
<br><br>
<div align=center src_cetemp="/images/cppblog_com/vczh/2010_Intellisense_NativeX_28.jpg"><img border=0 src="http://www.cppblog.com/images/cppblog_com/vczh/2010_Intellisense_NativeX_28.jpg"></div>
<br>&nbsp;&nbsp;&nbsp; Code snippet是一个比较复杂的功能，在这里我只实现了上下文无关的一些特性。Visual Studio里面的就更为高级了，他在插入一个类名的时候，可以根据上下文来判断是否需要出现完整的namespace。当然由于现在我用来做intellisense的语言还没有这么复杂的元素，所以也就无从做起了。<br><br>&nbsp;&nbsp;&nbsp; 所有的代码可以在<a style="TEXT-DECORATION: underline" href="http://vlpp.codeplex.com/" target=_blank>Vczh Library++ 3.0</a>里面找到。这篇文章的代码跟<a style="TEXT-DECORATION: underline" href="http://www.cppblog.com/vczh/archive/2010/11/07/132876.html" target=_blank>（十）</a>的时候的代码已经大为不同了。经过一系列的重构，intellisense功能最终被做成一个灵活的插件形式，所有的细节都可以更改，甚至连code snippet那个黄色的输入位置都可以再往上画东西。因为在开发intellisense的时候发现了NativeX语言语法上一个二义性，因此NativeX的语法也做了一处小修改。而且我趁着这个时候重构了所有NativeX的测试用例，把在C++里面构造语法树改成了用一系列make file和反射函数并运行的方式直接使用NativeXProvider编译、运行main函数并与记录在index文件中的结果进行比较的方法。<br><br>&nbsp;&nbsp;&nbsp; 接下来可以做的事情就比较多了。首先我要开始完成NativeX的调试功能，其次我要拿Microsoft SQL Server那个超级复杂的T-SQL来研究一个从文法产生intellisense代码（是的，你没看错，是intellisense代码）的方法，最后要在intellisense上实现NativeX的重构。对于那个自动生成intellisense代码的算法，目前还仅仅出于YY阶段，当然生成出来的也只能是上下文无关的intellisense。譬如说帮你列出数据库里面的所有表啦，或者帮你列出前面声明过的变量啦，不属于自动生成的范围。不过自动生成的intellisense还是会告诉你&#8220;现在需要弹出变量列表，需要提供内容&#8221;的这样一种信息来让你可以往里面添加一些不能自动生成的东西。当然做不做得出来那是另外一回事了，先研究研究。<br>
<img src ="http://www.cppblog.com/vczh/aggbug/135505.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2010-12-05 10:59 <a href="http://www.cppblog.com/vczh/archive/2010/12/05/135505.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发自己的IDE（十一），智能提示关键步骤揭秘</title><link>http://www.cppblog.com/vczh/archive/2010/11/22/134302.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Mon, 22 Nov 2010 11:29:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2010/11/22/134302.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/134302.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2010/11/22/134302.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/134302.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/134302.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp; 有两个星期没有更新博客了，主要是最近在研究一种更灵活的代码编辑框的框架设计，修了很多bug，还有公司的事情多了起来。现在全部都解决了，因此开始写这一篇博客。<a style="TEXT-DECORATION: underline" href="http://www.cppblog.com/vczh/archive/2010/11/07/132876.html" target=_blank><font color=#800080>上一篇</font></a>文章提到了我搞定了一个智能提示的原型，当然现在已经在<a style="TEXT-DECORATION: underline" href="http://vlpp.codeplex.com/" target=_blank><font color=#800080>Vczh Library++ 3.0</font></a>上面添加了鼠标指向一个对象显示声明代码和打括号的时候提示函数参数（部分完成）的功能了。今天来说一下我是如何实现这些功能的。当然我不会讲所有细节，只会讲重点，如何实现那个界面也不包括在这里。我要说的是，如何立刻知道任意一个位置所在的代码究竟是什么东西。<br><br>&nbsp;&nbsp;&nbsp; 如果你没有读过之前的几篇文章的话，建议去翻一翻，因为我之前提到了一些背景，还有我实现的<a style="TEXT-DECORATION: underline" href="http://www.cppblog.com/vczh/archive/2010/10/17/130202.html" target=_blank><font color=#800080>C#版yacc</font></a>（当然只是指功能，并不兼容），<a style="TEXT-DECORATION: underline" href="http://www.cppblog.com/vczh/archive/2010/10/23/130969.html" target=_blank><font color=#800080>IDE和编译器的语法分析器的异同</font></a>和实现一个IDE用的语法分析器<a style="TEXT-DECORATION: underline" href="http://www.cppblog.com/vczh/archive/2010/11/06/132654.html" target=_blank><font color=#800080>要注意的地方</font></a>。<br><br>&nbsp;&nbsp;&nbsp; 语法分析总是产生语法树或者分析树的，无论开发什么能够感应代码内容的工具，都逃不过语法分析。因此可以肯定的是，在你敲代码的时候，IDE真的在背后生成了一棵树，只不过为了要达到普通文本框的输入性能，很多东西都要移动到后台去做，但是为了瞬间响应并作智能提示，有一些东西要移动到前台做。他们之间的分界线想要界定清楚其实也不是很难。<br><br>&nbsp;&nbsp;&nbsp; 假设我们要编辑一份超大文件（几万行吧，再超过要开除的哈），每当你打字修改它的时候，一定会进行语法分析并产生语法树。对于这么大规模的代码要产生语法树肯定不是瞬间就能完成的（我那个东西大概要一秒钟多一点），因此这一步是在后台完成的。但是当你打一个"."的时候，你肯定希望立刻就要弹出列表的内容。为了知道列表的内容，你肯定得先知道那个"."出现在了什么表达式里面，以及"."前面的那个表达式究竟是什么类型，这是离不开全文分析的。但是全文分析又太慢，所以我引入了一个技术。<br><br>&nbsp;&nbsp;&nbsp; 为了完成这个技术，你必须在前台分析得到那个表达式。我们很容易就知道，我们是不可能等待后台分析给我们提供数据的。所以在这里我们要做的是，缓存当前我们感兴趣的代码。在这里简单化一下，如果我们只需要提供按"."弹出列表的话，我们只需要缓存语句（statement）就可以了。怎么做呢？假设我们已经可以通过所在的位置得到代码的内容（下面会讲），那么我们显然可以知道光标的位置所在的语句的语法树对象究竟是什么。有了这个语法树对象，我们就可以从代码里面直接把这个语句的代码文字复制出来，然后缓存语句的代码、语句所在的全文位置和语句所在的作用域。作用域是语法树的一部分，在做完语法分析之后，只需要做简单的语义分析建立作用域就可以计算很多东西了。<span style="COLOR: red"><strong>这个缓存会在光标位置移动的时候更新，也会在当前的全文分析结束的时候更新</strong></span>。<br><br>&nbsp;&nbsp;&nbsp; 一旦缓存下来之后，你往里面打了一个字符，那我不仅可以更新文本框里面的内容，我还可以更新缓存里面的代码的内容，同时还可以知道新的缓存开始结束位置。一个语句通常都是很短的，最多也就一百来个字符，因此我们立刻在前台对它做语法分析。而且往一个语句里面打字的话，99%以上的情况是不会影响到上下文的，所以<span style="COLOR: red"><strong>这个语句的旧作用域对象仍然可用</strong></span>。这个时候我们用旧的作用域对象来对新的语句做语义分析，那么就可以知道这个语句每一个表达式的类型了，从而知道了"."前面的表达式究竟是什么类型。然后利用旧作用域对象，我们就可以知道这个类型包含了多少成员。到了这一步，列表里面的对象就构造完毕了。<br><br>&nbsp;&nbsp;&nbsp; 然而后台的全文分析总是会结束的，所有的信息在这个时候就准备好了，然后发个消息给前台让它更新缓存。两种更新缓存都是用GUI的消息驱动的，所以不可能同时发生，只会先后发生。之前谈到的临时更新跟后台的全文分析是并行的，不过这个不会影响我们。只要我们正确处理后台跟前台的信息交换，那么整个智能感应的计算过程就可以做得十分安全，不会发生死锁。我相信这一点应该不是很难。<br><br>&nbsp;&nbsp;&nbsp; 那么，现在回到了两个最原始的问题。第一个是如何通过位置查找语法树。这个很容易解决，只要在语法分析的时候把所有跟位置有关的信息都记录在树里面就可以了。第二个问题是我们如何处理用户写错的代码。平时编译原理里面所教授的自动错误恢复其实是不好用的，你看看VC++的编译器在你写错了什么东西之后，大部分的错误信息基本上都没法看，因此如何进行错误恢复肯定要我们自己进行精心设计。但是问题来了，我们如何实现它呢？显然手写语法分析器会让我们心烦意乱根本做不下去（还要处处记得记录位置信息&#8230;&#8230;），因此我们需要一个语法分析器生成器。<br><br>&nbsp;&nbsp;&nbsp; 在这里我建议大家去阅读我博客上的两篇文章，你可以从这两篇文章所给的链接看到一些其他的东西，讲的是如何用<a style="TEXT-DECORATION: underline" href="http://www.cppblog.com/vczh/archive/2010/09/02/125621.html" target=_blank><font color=#800080>组合子</font></a>开发<a style="TEXT-DECORATION: underline" href="http://www.cppblog.com/vczh/archive/2008/05/21/50656.html" target=_blank><font color=#800080>语法分析器</font></a>。我这里给语法树添加了一个新属性，也就是一种组合起来强大但是又容易指定的错误恢复技术了。这里的错误恢复技术分为两种，一种是针对循环的，这个大家看代码就可以了，因为跟第二种——也就是序列关系的文法的错误恢复——非常相似，只是一个理论上的变换而已。<br><br>&nbsp;&nbsp;&nbsp; 内容是这样的。假设我们需要分析下面的表达式：EXPRESSION + "." + MEMBER，那么我们总是希望在残缺不全的代码里面恢复出尽可能正确的信息。我们知道一旦出现了"."，用户想要写的必然是一个访问对象成员的表达式，因此我们在"."那里表上记号，变成EXPRESSION + <span style="COLOR: red"><strong>"."</strong></span> + MEMBER。标记有一个副作用，也就是一旦标记所包含的语法分析成功了，那么整条语法会保证产生出指定的语法树结构。如果用户出现了错误，那么<span style="COLOR: red"><strong>所有的错误都会被当成用户少输入了什么东西而引起的</strong></span>。虽然这一个假设对于编译器来说不太合适，但是对于IDE来说显然是合适的。但是这种做法很容易在分析列表结构的代码里引起死循环，所以需要做很多测试来保证你的标记不会造成问题。<br><br>&nbsp;&nbsp;&nbsp; 下面的例子也可以辅助说明这种方法的有效性。举个例子，你需要做一个函数。你在写函数的过程中显然会临时或者不小心少些一些东西——有时候我们并不是把所有的事情都想清楚了才开始写代码的。这个时候为了正确分析出函数的结构，我们做下面的语法并标记：<br>&nbsp;&nbsp;&nbsp; FUNCTION_DECLARATION ::= TYPE + NAME + <span style="COLOR: red"><strong>"("</strong></span> + list&lt;TYPE + NAME, ","&gt; + ")" + COMPOSITE_STATEMENT<br>&nbsp;&nbsp;&nbsp; VARIABLE_DECLARATION ::= <span style="COLOR: red"><strong>TYPE</strong></span> + NAME + optional("=" + EXPRESSION) + ";"<br>&nbsp;&nbsp;&nbsp; 然后总是保证FUNCTION_DECLARATION的优先级比VARIABLE_DECLARATION更高，我们就总是可以恢复出最正确的语法结构了。这一种做法对于你在<span style="COLOR: red"><strong>连续输入代码的过程中进行正确的提示</strong></span>是相当好用而且方便的。<br><br>&nbsp;&nbsp;&nbsp; 至于代码生成器本身怎么实现，还是去<a style="TEXT-DECORATION: underline" href="http://vlpp.codeplex.com/" target=_blank>Vczh Library++ 3.0</a>下载代码吧。</p>
<img src ="http://www.cppblog.com/vczh/aggbug/134302.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2010-11-22 19:29 <a href="http://www.cppblog.com/vczh/archive/2010/11/22/134302.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发自己的IDE（十），我终于搞定了智能提示了哇哈哈</title><link>http://www.cppblog.com/vczh/archive/2010/11/07/132876.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Sun, 07 Nov 2010 11:11:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2010/11/07/132876.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/132876.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2010/11/07/132876.html#Feedback</comments><slash:comments>23</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/132876.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/132876.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要:     今天我终于实现了伟大的智能提示了，真是浑身上下都在发光啊。这次智能提示的代码可以在Vczh Library+ 3.0的页面上看到。我使用了上一篇文章所提到的技术，在用户输入文字的时候，通过迅速获得“当前编辑语句”的语法树，再加上旧的“当前编辑语句”的作用域对象，来判断用户究竟处于整份代码的什么地方，最后给出正确的提示。<br>&nbsp;&nbsp;<a href='http://www.cppblog.com/vczh/archive/2010/11/07/132876.html'>阅读全文</a><img src ="http://www.cppblog.com/vczh/aggbug/132876.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2010-11-07 19:11 <a href="http://www.cppblog.com/vczh/archive/2010/11/07/132876.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发自己的IDE（九）</title><link>http://www.cppblog.com/vczh/archive/2010/11/06/132654.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Sat, 06 Nov 2010 04:54:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2010/11/06/132654.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/132654.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2010/11/06/132654.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/132654.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/132654.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 隔了两个星期才更新，主要是因为之前有一个星期我拿来做了一个Ribbon的DEMO，将来打算用Ribbon来做IDE。另一个原因是这次去的的重大突破消耗了我整整一个星期的时间来完成，好久没有遇到这么困难的问题了&#8230;&#8230;<br><br>&nbsp;&nbsp;&nbsp; 这次主要解决的问题有两个。第一个是如何从文法生成一个可以对付残缺不全的代码的语法分析器，当然这个已经被很多论文研究过无数遍了，我就不详细解释了。第二个是如何高效的进行分析。我们知道当代码高达10000行的时候，语法分析再怎么快也得花上几秒钟时间（C#写的，已经很快了，何况这段代码是生成的&#8230;&#8230;）的。但是用户在按下&#8220;-&gt;&#8221;的时候根本来不及等你这么几秒，所以我想到了一个方法。<br><br>&nbsp;&nbsp;&nbsp; 用户写代码的时候总是会陷入思考的，这个时候后台的全文分析会跟上来，然后标记出&#8220;当前编辑语句&#8221;部分。如果你接下来快速输入，我除了再次启动后台的全文分析之外，我还会针对用户的输入来修改&#8220;当前编辑语句&#8221;的字符串然后针对这小小的几行代码用语法分析产生一个语句列表。这样的话UI线程里面的语法分析就快到可以忽略了，而且每隔几秒钟后台的全文分析就会赶上然后替换最新结果。这样可以保证你在打代码的时候有99%的概率我的语义分析可以正常工作。就算不能工作，也就是产生不出那个下拉列表，一般来说，这种情况只有那些打字的APM超过500的人才会碰到，正常人是不会碰到的&#8230;&#8230;<br><br>&nbsp;&nbsp;&nbsp; 介绍了原理之后，我就来贴张图了。不过在我这个Demo里面你真的输入10000行代码还是会感觉到延迟的，那是因为我为了调试，在Tree里面每次都会产生一颗平均十几万行的文本表示的全文语法树，Windows的那个文本框性能太烂了&#8230;&#8230;<br><br>&nbsp;&nbsp;&nbsp; 就贴几张图好了，首先是输入object，然后输入-&gt;，最后输入member;。写到-&gt;的时候已经出现了NativeXPointerMemberExpression了，下拉列表的所有信息已经完全出来了，哇哈哈。<br><br>
<div align=center src_cetemp="/images/cppblog_com/vczh/2010_CodeBox_11.jpg"><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/vczh/2010_CodeBox_11.jpg" width=683 height=287></div>
<br>
<div align=center src_cetemp="/images/cppblog_com/vczh/2010_CodeBox_12.jpg"><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/vczh/2010_CodeBox_12.jpg" width=683 height=287></div>
<br>
<div align=center src_cetemp="/images/cppblog_com/vczh/2010_CodeBox_13.jpg"><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/vczh/2010_CodeBox_13.jpg" width=683 height=287></div>
<br>
<div align=center src_cetemp="/images/cppblog_com/vczh/2010_CodeBox_14.jpg"><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/vczh/2010_CodeBox_14.jpg" width=683 height=287></div>
<img src ="http://www.cppblog.com/vczh/aggbug/132654.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2010-11-06 12:54 <a href="http://www.cppblog.com/vczh/archive/2010/11/06/132654.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发自己的IDE（八）</title><link>http://www.cppblog.com/vczh/archive/2010/10/23/130969.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Sat, 23 Oct 2010 04:34:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2010/10/23/130969.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/130969.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2010/10/23/130969.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/130969.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/130969.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;&nbsp;&nbsp;使用了上一篇文章的方法，我已经用C#把NativeX语言的语法分析器写出来了。而且最近把代码文件重构了一遍，删除掉了原来的实验性工程，转而重新设计了一个比较合理的工程结构，当然还是提交到了Vczh Library++ 3.0的页面上去了。现在先来看一看给IDE使用的文法哈。现在语法分析器已经有两套了，一套是C++写的用于开发NativeX的编译器的，...&nbsp;&nbsp;<a href='http://www.cppblog.com/vczh/archive/2010/10/23/130969.html'>阅读全文</a><img src ="http://www.cppblog.com/vczh/aggbug/130969.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2010-10-23 12:34 <a href="http://www.cppblog.com/vczh/archive/2010/10/23/130969.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发自己的IDE（七）</title><link>http://www.cppblog.com/vczh/archive/2010/10/17/130202.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Sun, 17 Oct 2010 09:51:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2010/10/17/130202.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/130202.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2010/10/17/130202.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/130202.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/130202.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;&nbsp; 在词法分析器生成器写完之后，就要做语法分析器的生成器了。今天完成了生成器的第一个版本。这个语法分析器生成器所做的事情就是从一个C#写的文法产生出C#写的该文法对应的语法分析器。在写文法的时候你需要提供每一个文法的返回类型，以及指定每一个属性究竟对应着文法的哪一段。为了方便，我提供了预定义的列表文法和左递归文法。当然我们知道手写递归下降分析器都是有套路的，用人写...&nbsp;&nbsp;<a href='http://www.cppblog.com/vczh/archive/2010/10/17/130202.html'>阅读全文</a><img src ="http://www.cppblog.com/vczh/aggbug/130202.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2010-10-17 17:51 <a href="http://www.cppblog.com/vczh/archive/2010/10/17/130202.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发自己的IDE（六）</title><link>http://www.cppblog.com/vczh/archive/2010/10/15/129946.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Thu, 14 Oct 2010 16:23:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2010/10/15/129946.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/129946.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2010/10/15/129946.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/129946.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/129946.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;&nbsp; 词法分析器生成器终于做好了，因此我又画了一个状态机然后生成了一个词法分析器，因此开始研究IDE的智能提示的技术了。智能提示的技术有几个要点，第一个是无论怎么慢都不能妨碍你打字，第二个是崩溃了也不能让IDE关掉，要重启分析器。因此我做了一个小实验。首先我将NativeX语言的着色器跟词法分析器都做好了，因此我要做的事情就是在你打字的时候，用另外一个线程进行词法分...&nbsp;&nbsp;<a href='http://www.cppblog.com/vczh/archive/2010/10/15/129946.html'>阅读全文</a><img src ="http://www.cppblog.com/vczh/aggbug/129946.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2010-10-15 00:23 <a href="http://www.cppblog.com/vczh/archive/2010/10/15/129946.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发自己的IDE（五）</title><link>http://www.cppblog.com/vczh/archive/2010/10/08/129087.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Fri, 08 Oct 2010 14:05:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2010/10/08/129087.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/129087.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2010/10/08/129087.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/129087.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/129087.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;&nbsp; 休息了大半个月，没写自己的代码了。国庆过了，再不为自己写写代码就有负罪感了。这篇文章所提到的所有工具的代码都可以在Vczh Library++ 3.0的页面找到。上一篇文章提到了一个状态机绘图工具，直到最近我终于把他的第一个部分给做好了。现在可以画图之后产生一个可以供这里所描述的高亮控件所使用的着色器了。第一步我们要使用TokenizerBuilder绘制一个...&nbsp;&nbsp;<a href='http://www.cppblog.com/vczh/archive/2010/10/08/129087.html'>阅读全文</a><img src ="http://www.cppblog.com/vczh/aggbug/129087.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2010-10-08 22:05 <a href="http://www.cppblog.com/vczh/archive/2010/10/08/129087.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发自己的IDE（四）</title><link>http://www.cppblog.com/vczh/archive/2010/09/20/127104.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Sun, 19 Sep 2010 17:58:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2010/09/20/127104.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/127104.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2010/09/20/127104.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/127104.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/127104.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 接着<a style="TEXT-DECORATION: underline" href="http://www.cppblog.com/vczh/archive/2010/09/18/126937.html" target=_blank>上一篇</a>的话题。开发智能提示首要的问题就是开发一个高性能的语法分析器。一个高性能的语法分析器总是包含一个高性能的词法分析器的。本系列的<a style="TEXT-DECORATION: underline" href="http://www.cppblog.com/vczh/archive/2010/09/16/126706.html" target=_blank>第一篇</a>已经提到了用C#和状态机写着色器对10万行代码进行着色只需要半秒。鉴于我们大部分的程序文件都只是几千行，因此用相同的技术开发的词法分析器显然可以在几十毫秒内完成对文件的分析，从而再也不需要担心词法分析器的性能问题了。<br><br>&nbsp;&nbsp;&nbsp; 着色器的状态机一般都比词法分析器的状态机简单，因为我们总是使用一个颜色来表达一些类型的记号（譬如操作符、数字和名字一般都用同样的颜色——黑色）。因此我们每当支持一种新语言或者当语言升级的修改IDE的时候，总是要同时修改两个状态机。手写状态机是很容易出错的，就如同手写语法分析器也很容易出错一样。语法分析器的解决办法是让你给文法来生成语法分析器的代码，因此词法分析器和着色器也使用类似的方法：给状态机生成代码。<br><br>&nbsp;&nbsp;&nbsp; 目前这个状态机只做了一半：只能画状态，暂时还不能指定颜色或者记号类型。当然添加一个指定颜色的功能是很简单的，不过我还需要想一想如何用图像来表达，让状态机显得更清晰。今天做了一个晚上搞定了状态机的编辑程序，如图所示：<br>
<div align=center src_cetemp="/images/cppblog_com/vczh/2010_CodeBox_02.jpg"><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/vczh/2010_CodeBox_02.jpg" width=681 height=768></div>
<br>&nbsp;&nbsp;&nbsp; 接下来就可以开发两个功能，第一个是生成着色器的代码，第二个是生成词法分析器的代码。这样就可以避免因为程序写错从而省下一大堆调试的时间了。
<img src ="http://www.cppblog.com/vczh/aggbug/127104.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2010-09-20 01:58 <a href="http://www.cppblog.com/vczh/archive/2010/09/20/127104.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发自己的IDE（三）</title><link>http://www.cppblog.com/vczh/archive/2010/09/18/126937.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Fri, 17 Sep 2010 16:43:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2010/09/18/126937.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/126937.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2010/09/18/126937.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/126937.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/126937.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 今天来说一下智能提示的初步想法。智能提示需要解决的问题有两个。第一个是迅速知道光标位置在与编辑中的代码相对应的抽象语法树中的位置。第二个是把当前用户可以输入的东西显示出来并且提供输入的便利。第一个问题里面有两个小问题，包括用你能达到的最快速度分析代码全文组成语法树并产生scope表，以及智能地在用户输入东西的时候临时对输入的那一小块（如何确定块的区域，这个根据不同的语言以及编辑的不同位置可能需要不同的算法）进行重新分析产生一棵小树。我们总是可以在全文分析没结束之前，使用上一次全文分析产生的scope表以及这棵小树来得到超过99%正确率的上下文。<br><br>&nbsp;&nbsp;&nbsp; 那么今天要说的就是如何用C#进行高效的全文分析。我们知道全用LALR的话不仅难开发而且代码难调试难测试难修改，因此就算了。最好调试的代码是什么呢，显然是递归下降法写出来的。其实代码本来没多少层，所以递归下降最多也就递归十几层，也不会太多，总的来说性能还是可以接受的。但是每来一个语言就用一次递归下降还是很惨的。好在.net自带C#编译器，我们可以使用parser combinator来生成。关于什么是combinator，可以参考<a style="TEXT-DECORATION: underline" href="http://www.cppblog.com/vczh/archive/2010/09/02/125621.html" target=_blank>这里</a>。至于什么是parser combinator，我曾经用C++<a style="TEXT-DECORATION: underline" href="http://www.cppblog.com/vczh/archive/2010/04/28/113836.html" target=_blank>实现了一个</a>。<br><br>&nbsp;&nbsp;&nbsp; Parser combinator的好处是我们可以在C#里面把文法直接表达出来，然后变成一个语法分析器。不过直接执行combinator，性能会受到很大影响。怎么样才能把性能降低到跟手写的差不多呢？.NET给了我们三种武器，分别是CodeDom、Emit和Linq Expression。我比较倾向于CodeDom，CodeDom可以让我们写C#来拼出一颗巨大的代表一个C#程序的语法树，然后用自带的.net编译器去编译成dll或者cs文件。因此这个C#的parser combinator的目的就是要让我们用最美妙的语法来拼出目标语言的文法，最后根据文发来产生一份C#语法分析器的代码。我们可以每次运行的时候都编译出一个内存的dll，或者直接产生一个cs文件然后拖进我们的工程。<br><br>&nbsp;&nbsp;&nbsp; 我目前可能会采取前一种方法：也就是用parser combinator来产生文法树，然后我提供一个函数来把它转换成一份对应的C#递归下降语法分析器的代码（跟yacc很像哈，虽然他用的是LALR），最后编译它。因此只需要在IDE第一次打开某个语言的代码文件的时候编译出这个语法分析器，在IDE关掉之前就都可以用了。<br><br>&nbsp;&nbsp;&nbsp; 那语法分析器要产生什么语法树呢？这个还是要我们自己来解决的。不过我采取了一种比较偷懒的方法。我先写了一个语法树的基类（<a style="TEXT-DECORATION: underline" href="http://vlpp.codeplex.com/" target=_blank>vlpp.codeplex.com</a>后Candidate\CodeBoxControl\CodeBoxControl\CodeProvider\*.cs），然后只要你给我一个这样子的虚类：<br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #008080">&nbsp;1</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">abstract</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;ExpressionNode&nbsp;:&nbsp;CodeNode<br></span><span style="COLOR: #008080">&nbsp;2</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;{<br></span><span style="COLOR: #008080">&nbsp;3</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}<br></span><span style="COLOR: #008080">&nbsp;4</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;5</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">abstract</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;NumberNode&nbsp;:&nbsp;ExpressionNode<br></span><span style="COLOR: #008080">&nbsp;6</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;{<br></span><span style="COLOR: #008080">&nbsp;7</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;Number&nbsp;{&nbsp;</span><span style="COLOR: #0000ff">get</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #0000ff">set</span><span style="COLOR: #000000">;&nbsp;}<br></span><span style="COLOR: #008080">&nbsp;8</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}<br></span><span style="COLOR: #008080">&nbsp;9</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">10</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">abstract</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;AddNode&nbsp;:&nbsp;ExpressionNode<br></span><span style="COLOR: #008080">11</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;{<br></span><span style="COLOR: #008080">12</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">abstract</span><span style="COLOR: #000000">&nbsp;ExpressionNode&nbsp;Left&nbsp;{&nbsp;</span><span style="COLOR: #0000ff">get</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #0000ff">set</span><span style="COLOR: #000000">;&nbsp;}<br></span><span style="COLOR: #008080">13</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">abstract</span><span style="COLOR: #000000">&nbsp;ExpressionNode&nbsp;Right&nbsp;{&nbsp;</span><span style="COLOR: #0000ff">get</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #0000ff">set</span><span style="COLOR: #000000">;&nbsp;}<br></span><span style="COLOR: #008080">14</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br>&nbsp;&nbsp;&nbsp; 那么你就可以用CodeNode.Create&lt;AddNode&gt;()或者CodeNode.Create&lt;NumberNode&gt;()来获得相应的实现了。至于CodeNode的声明是这样的：<br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #008080">&nbsp;1</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">abstract</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;CodeNode<br></span><span style="COLOR: #008080">&nbsp;2</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;{<br></span><span style="COLOR: #008080">&nbsp;3</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;TextPosition&nbsp;Start&nbsp;{&nbsp;</span><span style="COLOR: #0000ff">get</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">internal</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">set</span><span style="COLOR: #000000">;&nbsp;}<br></span><span style="COLOR: #008080">&nbsp;4</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;TextPosition&nbsp;End&nbsp;{&nbsp;</span><span style="COLOR: #0000ff">get</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">internal</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">set</span><span style="COLOR: #000000">;&nbsp;}<br></span><span style="COLOR: #008080">&nbsp;5</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;CodeNode&nbsp;ParentNode&nbsp;{&nbsp;</span><span style="COLOR: #0000ff">get</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">internal</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">set</span><span style="COLOR: #000000">;&nbsp;}<br></span><span style="COLOR: #008080">&nbsp;6</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;CodeNodeCollection&nbsp;Nodes&nbsp;{&nbsp;</span><span style="COLOR: #0000ff">get</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">set</span><span style="COLOR: #000000">;&nbsp;}<br></span><span style="COLOR: #008080">&nbsp;7</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;ICodeScope&nbsp;OwningScope;<br></span><span style="COLOR: #008080">&nbsp;8</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;ICodeScope&nbsp;Scope;<br></span><span style="COLOR: #008080">&nbsp;9</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">10</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;CodeNode();<br></span><span style="COLOR: #008080">11</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">12</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;T&nbsp;Create</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">()<br></span><span style="COLOR: #008080">13</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;where&nbsp;T&nbsp;:&nbsp;CodeNode;<br></span><span style="COLOR: #008080">14</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br>&nbsp;&nbsp;&nbsp; 因此当你往AddNode.Left赋值的时候，也就是等于在写CodeNode.Nodes["Left"]，这就是Create&lt;T&gt;所提供的实现了。当然写进去了之后ParentNode和Scope属性就会立刻有效了。这种方法还是可以剩下你不少时间的。<br><br>&nbsp;&nbsp;&nbsp; 今天就说到这里了，然后我就得去开发那个C#的parser combinator并且想好一个单元测试的对策（这也是一种练习哈），然后再继续写博客了。不过中秋节那一整个星期都要回家办点事情所以估计会暂停。
<img src ="http://www.cppblog.com/vczh/aggbug/126937.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2010-09-18 00:43 <a href="http://www.cppblog.com/vczh/archive/2010/09/18/126937.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发自己的IDE（二）</title><link>http://www.cppblog.com/vczh/archive/2010/09/17/126817.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Thu, 16 Sep 2010 18:32:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2010/09/17/126817.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/126817.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2010/09/17/126817.html#Feedback</comments><slash:comments>12</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/126817.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/126817.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 今天先放图哈。智能完成已经开始做试验了不过距离能看还差很远，所以今天先继续谈一下着色的事情。<br>
<div align=center src_cetemp="/images/cppblog_com/vczh/2010_CodeBox_01.jpg"><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/vczh/2010_CodeBox_01.jpg" width=718 height=660></div>
<br>&nbsp;&nbsp;&nbsp; 这就是我暂时实现的所有功能了。首先着色算法可以外挂，其次左边那个边栏（大小和绘制均可以订制）操作他的时候会发生什么事情也是外挂的。着色器与&#8220;断点变红&#8221;是分离在两个不同的插件接口里面的，原因<a style="TEXT-DECORATION: underline" href="http://www.cppblog.com/vczh/archive/2010/09/16/126706.html" target=_blank>上一篇文章</a>说过了。你们可能还会注意到那个灰色的框框。那个框框的确是会被编辑器当成一个整体来对待，不过我绝对<span style="COLOR: red"><strong>还没有实现折叠</strong></span>。因为在我的设计里面，如何进行折叠应该是插件的事情，控件本身只要处理好怎么编辑和显示就行了。还有一个比较难发现的就是，我这玩意儿也是支持输入法的，输入法的窗口会跟随光标移动&#8230;&#8230;<br><br>&nbsp;&nbsp;&nbsp; 在开发这个东西的时候我尝试了两种新方法。第一种是MVC。MVC开发高亮还真是容易啊，不仅文字缓存部分（C#也是可以精确控制内存的哈）可以独立出来，连编辑操作（各种按键鼠标组合）其实也可以不做在控件里面。这样有什么好处呢，当然是可以进行高强度的单元测试了哈。第二种就是GUI自动化，光对类进行单元测试还是不够的，Visual Studio 2010为.net单元测试工程提供了一个Coded UI Test框架可以给我启动一个独立的外部程序（MFC写的也行，WinForm写的也行，WPF写的也行，网页都行）然后操作上面的各种控件最后拿到控件里面的信息。不过可惜的是我的文本框并没有按照Windows的UI Automation标准来实现（从而让盲人也能使用这个控件），因此只能进行键盘和鼠标的操作，至于我绘制的东西是什么则需要其他方法。C#跨进程怎么做最方便呢？当然是Windows Communication Foundation了哈。为了写足够的单元测试是要不惜一切D。不过显然WCF的服务不可能做在控件里，因此我的solution下面暂时就有控件工程、测试工程和被测试的&#8220;独立程序&#8221;工程了。<br><br>&nbsp;&nbsp;&nbsp; 有了GUI自动化测试，我在进行重构的时候，就可以放心的修改代码，然后执行测试程序，去外面喝杯茶。过个几分钟测试工程就会跟我报告一共挂掉了多少个case，只要修好就行了。这种方法杜绝了绝大多数由粗心引起的bug。如果你在公司使用类似技术来对付你的代码的话可以有效减少工作时间，从而让公司可以榨取更多价值。<br><br>&nbsp;&nbsp;&nbsp; 操作的组合还是比较麻烦的。为了全套支持，我特地操作了一下Visual Studio 2010的文本框，然后对一些我看不顺眼的行为经过修改之后，现在已经可以实现{LEFT, RIGHT, UP, DOWN, HOME, END, PAGEUP, PAGEDOWN, ENTER, BACKSPACE, DELETE}&#215;{null, CONTROL, SHIFT, CONTROL+SHIFT}共44种操作方法。加上鼠标，突破半百。这么复杂的东西，如果没有足够的单元测试，也没有足够的GUI自动化测试的话，随便改个什么都很有可能发生问题的。所以开发这类程序的时候要十分小心，一定要写单元测试。<br><br>&nbsp;&nbsp;&nbsp; 至于着色应该怎么测试呢？只要有了WCF，就十分简单了。测试程序发送两个坐标，WCF服务返回坐标之间所有字符的颜色代号就行了。代号是可以在测试程序跟被测程序之间约定的，所以这种方法就让测试变得十分简单了。<br><br>&nbsp;&nbsp;&nbsp; 开发这一部分一共花掉我大约四天时间（假设不用上班每天能写8个小时，累计出来的）。当然平时要上班所以实际花费是要多一倍不止的。其实当我在纸上画出了上图C#着色器的状态机之后，也没想到实现出来速度这么猛的。虽然着色器使用状态机来实现已经是速度最快的方法了（经过大学4年写编译器的经验&#8230;&#8230;不过我后来用C++做出了一个能根据正则表达式在内存中产生词法分析器的，比手写的更快），不过还是要感叹一下.net到了4.0还是比起当年的2.0要进步无穷多倍的哈。虚拟机可以在执行的时候才开始产生并优化x86代码，可以让程序越跑越快（非骗人，编译原理小白请自行学习），这还是静态编译其所不能达到的。之前还看过channel9上面的视频讲微软某个研究院在做一个全新的javascript引擎（看起来好像没有加进IE9beta），就是用了动态的两阶段profile+optimize+codegen的方法，通过为瓶颈代码使用激进优化方法，从而让总体的运行和编译时间的总和降到最低。生成X86什么的还是非常麻烦的，总之我已经被机器码囧了半年，暂时不想碰JIT了&#8230;&#8230;当然这是迟早要再碰一次的。<br><br>&nbsp;&nbsp;&nbsp; 写到这里就先碎觉了，下一篇开始说之前在纠结的过程中产生的几个智能完成的方案。迟早都要把它给做出来的。
<img src ="http://www.cppblog.com/vczh/aggbug/126817.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2010-09-17 02:32 <a href="http://www.cppblog.com/vczh/archive/2010/09/17/126817.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发自己的IDE（一）</title><link>http://www.cppblog.com/vczh/archive/2010/09/16/126706.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Wed, 15 Sep 2010 16:19:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2010/09/16/126706.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/126706.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2010/09/16/126706.html#Feedback</comments><slash:comments>27</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/126706.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/126706.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; <strong>在写这篇文章的时候，我正在尝试自己开发一个我自己认为能拿出去见人的IDE。当然此时此刻我只开展了一点点工作。所以这篇文章没有什么最终的指导性，而是在记录我开发IDE的思考过程。当然我觉得之前写了那么多东西除了开了源之后介绍了我的作品让大家可以更好的理解并学习以外，其实也没有什么大的效果（除了几篇置顶的教程我个人觉得还是有点效果的&#8230;&#8230;）。因此我尝试做一下改变，把我的思考过程描述出来。一方面我自己可以从一个更高的高度来审视我自己，第二个就是如果你们想从我这里拿走什么，或者想教我什么，请自便哈。</strong><br><br>&nbsp;&nbsp;&nbsp; 其实以前并不是没有开发过IDE，只是那个IDE除了语法高亮以外什么都没有，因此其实并没有什么大的用处。个人认为IDE要提供给你的功能有三点：智能提示、集成调试、辅助部署。当然在我眼中最厉害的IDE当属VisualStudio了，各种功能真是非常人性化，而且也跟我的观点比较一致：我只是想开发个编译器然后开发个makefile系统让别人可以方便一点用我的编译器而已，为什么我一定要用makefile来组织我的编译器源代码啊，一点都不方便（噗<br><br>&nbsp;&nbsp;&nbsp; 是个程序员都是这么想的哈。<br><br>&nbsp;&nbsp;&nbsp; IDE还是好东西。前几天我在<a style="TEXT-DECORATION: underline" href="http://vlpp.codeplex.com/" target=_blank>vlpp.codeplex.com</a>上面checkin了一份我开发的语法高亮编辑器的雏形（下载后打开Candidate\CodeBoxControl\CodeBoxControl.sln），完全用C#写。我的Demo也是用的C#，外挂了一个可以分析C#的关键字、字符串和注释的代码着色器，在我的机器上（虽然我觉得比较强大，不过我的程序也是单核的，因此其实也只有2.7G的频率）着色一个将近10万行的程序只需要半秒钟。其实大家大可不必觉得C#很慢，其实是很快的，慢的是你的内心。<br><br>&nbsp;&nbsp;&nbsp; 当然我也做了一点优化，全文着色要半秒，不过其实你在编辑的时候是不需要总是全文着色的。所以我的着色器接口做了一点小限制：<br>&nbsp;&nbsp;&nbsp; 1、你必须用状态及实现，而且状态及的状态只能用int类型来表达。<br>&nbsp;&nbsp;&nbsp; 2、着色必须是上下文无关的。<br><br>&nbsp;&nbsp;&nbsp; 对于2可能比较难理解。首先C#那个可以检查出一个ID是不是一个类型然后变色其实根本不是着色器的任务（根据我的设计，你可以在另一个地方临时更改颜色，也能实现）。其次对于一个给定的任意字符串前缀，其着色效果不能跟前缀之后的任何字符有关系。<br><br>&nbsp;&nbsp;&nbsp; 因此我只需要记下每一行的末尾着色器当时的状态，就可以从任意位置开始到任意位置结束进行部分着色了。因此这里就有很多的优化空间。有了这些优化之后，我用我的Demo编辑一个将近10万行的C#文件的时候，那个运行在UI线程里面的着色算法丝毫没有让我觉得有延迟，只有在少数情况下（瞬间贴了好几万行代码，然后按ctrl+end跳到全文最后，我不得不对你贴进去的东西立刻着色）才会让你感觉到有小于半秒钟的延迟。所以我觉得这个设计已经可以达到我的要求了，因为我自己写的代码一般单个文件都没有超过1万行，所以偶尔给我一个小于0.05秒的延迟其实也是无所谓的&#8230;&#8230;<br><br>&nbsp;&nbsp;&nbsp; 为什么可以进行优化呢。你可以想一下，如果我正在对某一行进行编辑，而且这一行后面的代码都已经被着色过了，那么如果你的改动都没有让行末尾的着色器状态发生变化，那么这一行后面的所有字符都不需要更改他的着色，因此我就可以只对你当前编辑的一行进行着色（唯一修改的其实也就只有那种多行注释，你一般也不会写很多这种多行注释的，都用的单行&#8230;&#8230;）。一百来个字符的着色基本上可以忽略，因此无论你的文件有多大，其实着色速度是跟你平均每行的长度有关系，只有在极少数情况下才会跟你的行数有关系。这个时候你可以看到着色器两个限制的强大威力了吧。<br><br>&nbsp;&nbsp;&nbsp; 那么，当我们对一行代码进行断点的时候，代码颜色的修改是如何做的呢？为了这个东西去影响着色器那个强到可以忽略的效率实属杀鸡取卵，所以答案就是：外挂一个控制面板接口，让你可以在显示某一行的时候临时修改那一行每个字符颜色。听起来好像很影响效率，不过我们要相信，一行代码也就只有那么几十到一百来个字符，一屏幕的代码最多也就一两千个字符。任何语言无论多慢，对一个一两千那么长的数组赋值，也是奇快无比的，何况是C#这么快的语言&#8230;&#8230;<br><br>&nbsp;&nbsp;&nbsp; 因此我们剩下的问题就是如何实现一个可以修改文字颜色的普通文本框了哈。经过我的3此研究，结论就是，不要用RichTextBox，你自己自绘从头写一个。第二个结论，凡是GUI最好都别用C++，无论GUI类库多么好，一个没有内存管理器就足以让你觉得很麻烦了，当然对于编译器本身我还是推荐C++的，因为编译器虽然算法复杂，不过结构简单，所有的内存分配都是可以预测的，因此delete起来非常有信心。<br><br>&nbsp;&nbsp;&nbsp; 最近一两个星期都在纠结如何实现一个简单的上下文有关的智能提示功能（至少按个"."会有个列表什么的）。这个明天再写了，今天只有一点点头绪，还没完全成型。
<img src ="http://www.cppblog.com/vczh/aggbug/126706.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2010-09-16 00:19 <a href="http://www.cppblog.com/vczh/archive/2010/09/16/126706.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>