随笔-341  评论-2670  文章-0  trackbacks-0
 
     摘要:     做编译器难免会遇到如何输出错误信息的问题。这个问题不是那么好解决的,因为有可能你需要提供中文、英文还是其他什么乱七八糟语言的编译器。于是就要想一种办法让错误信息的语言可以很方便的切换。    由于同时提供中文和英文的编译器其实没什么用,中文的编译器和英文的编译器其实可以是两个不同的exe,因此我想写两个宏,譬如说中文.bat点击了...  阅读全文
posted @ 2010-03-05 23:08 陈梓瀚(vczh) 阅读(2498) | 评论 (2)编辑 收藏
    今天终于让脚本引擎可以成功执行int main(){return 1+1;}了,总算是让冒烟测试过了。一开始没那么顺利,主要是因为昨天写了一整天代码,犯了点错误。

    举个例子,脚本里面有return语句,这个语句实际上是对应到一个jump指令去,跳到函数最后面执行一些代码然后返回。当代码生成执行到return的时候,实际上函数还没生成完,所以不知道jump到什么地方。因此我想到一个办法,我先把这些需要延迟填充的指令都记下来,当函数结束的时候一次性填充它们。于是我使用了下面的代码:
1 needToReturns.Add(&ins[ins.Count()-1]);

    这样做我就把刚刚加入的那条jump指令,也就是当前的最后一条指令的指针添加到一个列表里面去了。于是我接着处理很多代码生成的工作,也免不了往needToReturns添加很多的其他jump指令。于是到了最后填充它们的时候了,没有问题发生。代码生成结束之后,我就要把再也不需要的符号表给析构掉,这个时候VC就报告说释放一个TypeInfo的时候完蛋了。

    其实很明显,错误肯定不会出现在TypeInfo里面的,想都不用想肯定是哪里溢出了。但是自己的容器的operator[]都有溢出检查的,不可能通过a[b]=c;的方法导致内存出现错误。最后想来想去,发现问题出在上面那一行代码里。

    我们都知道,一个列表在Add的时候,他所拥有的缓冲区肯定最终都会用光,然后分配一个更大的。于是之前的&ins[x]跟之后的&ins[x]的地址肯定不一样,于是我添加到needToReturns里面的引用就变成一个野引用了。搞定一个函数之后,我一下子填充它们,也就等于往各种各样不同的野引用里面写东西。刚好之后创建的一个符号表项就是某一个野引用指向的地方,于是内容就被我破坏了,释放它的时候当然就出现问题了。

    这个故事告诉我们
1:永远不要试图获取容器中某一个项的引用。
2:就算是自己的类,有时候也会误用的。

    关于我如何执行int main(){return 1+1;},可以在Vczh Library++3.0的单元测试代码(UnitTest\UnitTest\TestScripting_BasicLanguage_CodeGeneration.cpp)里面找到。
posted @ 2010-02-26 19:41 陈梓瀚(vczh) 阅读(2403) | 评论 (6)编辑 收藏
    已经差不多有三个多星期没有写博客了。这段时间主要是在休假。休假的时候写代码,跟朋友玩,去了趟广州办点事情,然后就要结束了。Vczh Library++3.0还是一直在开发。第一层语言的语义分析基本上已经做完了,不过还需要加一点功能进去,这在代码生成部分做了之后再做。

    这次的思路比之前清晰了很多。我一直在思考如何将Javascript编译成C#再编译成C语言的问题。当然这说的三种语言只是“类似”,无论换成python也好lua也好都是一样的。作为一个通用的脚本语言开发平台,我的目标是让一门新语言可以被很快的组装起来,并在我开发的平台上运行,与平台上的其他语言互通并共享函数库。前者是重点,后者是支撑。也就是说无论怎么定位,最终都是要做成一个可以让各种语言同时运行,并让新语言可以被快速开发的一个平台。于是每一层语言需要支持的特性都得经过深思熟虑才能确定下来。

    因此,我的策略是为各种不同类型的语言开发一个元语言,然后通过描述一门新语言与相应的元语言的不同之处来完成该新语言的开发工作。因此最近的工作主要都是针对最基础的那一层——也就是面向过程非托管语言。在这里大概可以称之为Native X语言吧。之所以叫Native X,是因为这玩意儿跟C语言还是有一些比较大的区别的。跟之前的CMinus不一样,CMinus致力于将C(做了一点修改)编译成x86的代码,而Native X则致力于提供大多数面向过程的非托管语言所需要的组件,并且为面向对象的托管语言提供必要的基础设施。

    在不断地思考中,我的目标已经渐渐清楚了。Native X语言与C语言的差别主要有以下部分:
    1、没有宏
    2、提供struct的构造函数和析构函数
    3、提供泛型
    4、提供dll级别的反射和泛型支持。
    5、源代码组织方法不体现在代码里(没有#include,或者#import之类的预编译指令)
    6、提供new和delete的变形

    也就是说,今后用Native X语言写的模板函数和模板类型,就算编译进dll也无所谓,照样可以被其他程序拿去使用。这样的话可以做很多事情,譬如说实现pascal和basic的数组和字符串,实现C#的类引用等等。但是Native X语言同时具有操作指针和内存的能力,虽然比较危险,但是可以通过上层语言的编译器来确保产生的代码是受保护的。这样做的好处是在Vczh Library++3.0开发完以后,一个游戏的脚本可以大部分用Lua来写,而且其中需要后门的一小部分代码可以用C来写,然后编译在一起成为一个独立的脚本程序,最后让游戏去执行它。

    上面描述的功能是在抽象了很多语言的语法之后确定下来的。这个目标在当前进度下实现了一般,也就是说这些多出来的部分还没做,但是C语言有的那一部分的语义分析已经做完了,顺带搞定了一个通用的符号表雏形,还有大大简化语法树以及相关算法开发的各种基础设施。目前的计划是先实现C语言有的那一部分的代码生成,让脚本可以运行起来,然后将上面列出来的“区别”一个一个添加进去,同时做好unit test的工作。

    最新的代码可以在http://vlpp.codeplex.com下载。
posted @ 2010-02-23 08:59 陈梓瀚(vczh) 阅读(2713) | 评论 (9)编辑 收藏
    也是cppblog一位同学写的文章,原文在这里

    其实总的来说这篇文章还是没什么大的问题,你看那五点粗字标题,就是在告诉你不仅要写好的程序,还要写有用的程序。不过进了公司老板很难给你写没用的程序的,这点就忽略了。紧扣着客户的需求写是好事,不过这跟广大的大学同学们还是没什么关系,所以最后一点就忽略了。咱慢慢看前面的四点。

    第一点说在校期间的实习是很重要的。这一点当然是对的,不过下面的论据有点问题。先看后面的。公司要能干活的人是真的,学计算机搞创新搞研究能拿奖那也是真的,只是拿的是图灵奖不是诺贝尔奖。这个诺贝尔奖有点问题啊,没有数学没有计算机(他老人家死得太早了,原谅他),所以数学和计算机就自立门户了。

    在校实习可以赚工作经验。为什么这么讲呢?(华南理工大学的师弟师妹在2009年的时候告诉我们,金山公司给实习,做的东西不会拿去卖的,你们做完就完了。道听途说,谢绝跨省。)一般大公司都会给你真刀真枪的东西。写的代码会被最终用户运行,修bug的结果也是被最终用户运行的。到时候会有一大堆人指导你该怎么做的,因为如果你写的代码太烂他们也不好意思把你的代码拿去用是不是。

   但是说学校教的东西与社会脱节就不好了。要我是校长肯定会拍案而起:“你们把学校当成什么东西了,Java速成班?”学校教的很多东西都是基础知识,根据《Teach yourself programming in 10 years(想看的自己去google)》,4年是远远不够成为一名优秀的程序员的。我们的确需要花大量的时间在基础课上面,譬如说掌握一两门语言和一点API让你们可以做出真正有用的东西啦,数据结构,网络,数据库,编译原理,操作系统原理,等等等等。但现在的事实是很多高三的学生们在填志愿的时候还不知道自己学了计算机就会上了贼船,所以大量的人是大一的人才开始写代码的。4年当然不够了,所以在学习基础课的时候,我们还需要自己给自己出点难题,写点代码。在我看来,学校只需要保证一个几乎把自己所有的时间投入到代码中去的人能够找到合理的工作就好了。谁让他花那么多时间玩游戏的(其实我也喜欢玩,但我不会没日没夜的,写完代码才会玩的),那将来结果不尽人意只能怪自己了。

    需要注意一点的是,上面那句话最后几个字是“合理的工作”而不是“理想的工作”。为什么呢?这跟你学了什么东西是很有关系的。找到理想的工作还要有一个前提,跟学校无关的,就是你要挖掘出自己的兴趣所在。你往那方面不短拼命练习,就可以保证你可以只找你喜欢的工作,找到了当然是理想的了。如果你并不是特别喜欢写代码,但是也成为了一名不错的程序员的话,那只能说是合理了。好工作,但你不喜欢罢了。

    先总结一下,学校教基础,实用的自己去学。至于那些理论课有什么用,当你一个工程的代码写到了好几万行而且里面绝大多数都不是用来处理UI和SQL的时候,你就能开始理解了

    第二点,思想周密谨慎。文章下面只有一句话,其实说是说对了,只是泛泛而谈也不能当指南来看。当然我并不是在批评作者,说不定人家本来就不想写指南,只是“读者有心”罢了。

    思想为什么要周密谨慎,因为计算机语言太低级,我们不得不去处理大量的其实跟我们要解决的问题没什么关系的细节。为了很好的掌控这些东西,就要学习学校教给你们的那些所谓与社会脱节的基础课啦。就跟学数学一样,就算你将来真的不用考计算微积分来吃饭,但好歹学那个东西还是提高了你的智商的。如果你有幸真的需要考那些基础课来混饭吃的话,那你就更会体会到它们的重要性了……说白了还是那句话,实践出真知啊。趁着还在读书的时候赶紧写代码,等到将来被HR鄙视就晚了。

    第三点,不要因为代码简单就不想写。文中的一个观点就是,同一个东西,你写的次数越多,你解决它的方法就越美妙。这就是为什么我们要不断地重写重构的原因了,代码速度越快,并且越容易维护的话,将来遇到需求变更你就不用觉得自己快死了一样。

    第四点就不评论了。记得在Channel9看一个叫eric的老头讲解haskell的时候,他不停的说要“Put your love in your code.”要有爱。为了能让自己的爱发挥作用,当然首先要让自己写出漂亮的代码了。

    总结:不要抱怨学校,高考志愿是你自己填的。总的来说文章的大道理还是对的,就是论据稍微有点什么,总之自己看着办吧。
posted @ 2010-01-31 03:44 陈梓瀚(vczh) 阅读(5218) | 评论 (10)编辑 收藏
    可扩展编译器架构的构想是最近几天在洗澡的时候才最终完成的。我在思考如何开发一个可以同时给C、Pascal、Basic、Fortran和未知的类似语言使用的前端+后端。这只是VL++3.0的其中一个小部分,我把语言归为几类,C一类,C#一类,Javascript一类,还有其他的等等。这些类型会分别提供不同的前端支持。在设计第一类的编译器期间遇到了点困难。

    第一个困难是语法树很难统一。其实这并不是说那些语言完全不同,而是在于我想让这N种语言的区别只有从字符串到语法树的部分,从语法树开始都执行相同的代码来编译。这就遇到了点麻烦。在语法分析的过程中,对于Pascal我不知道Name(Param)究竟是函数调用还是强制类型转换,对于Basic来说我不知道Name(Param)是函数调用还是数组下标。还有Pascal和Basic的and等操作符可以同时作用于整数和布尔型(C使用了&&和&,而且它们在实现上有巨大差别)。Pascal自己还扩展了一些类型譬如说set,Pascal和Basic还有字符串。所以在语法分析的时候很难构直接造出FunctionInvokeExpression、SubscribeExpression和TypeCastExpression。

    第二个困难是扩展的类型。上面提到了Pascal有自己的set,我如何让我的编译器从前端开始就可以应付一门类似的未知语言他自己的新东西。譬如说未知的set类型,他也有自己的操作符(连已经存在的操作符operator+也可以用的),代码生成的时候还有自己的方法。这不仅要求语法树是可扩展的,接下来的一切包括符号表、语义分析、代码生成等所有部分都需要可扩展的。

    第三个困难是C自己造成的,他有一个十分讨厌的地方。当我得到ABC*DEF;的时候,语义分析没开始,我不可能知道这是乘法还是定义一个变量。

    思考了许久,得出一个大概的方案:我先定义一门比较严格的语言,然后让C、Pascal、Basic和Fortran来定义自己与该语言的不同之处,从而尽可能复用编译器其余相同的部分。想到这里我得到一个比较奇怪的做法:

    第一个做法是在语义分析的时候修改语法树。对于C语言的ABC*DEF;,这是一个statement。我给出一个接口,这个接口在语义分析的过程中被调用。语义分析产生了大量的信息全部传递过去,然后再第一次接触到一个statement的时候,调用其中的ReplaceStatement函数。这个时候接口的ReplaceStatement可以通过语义分析的结果看看需不需要修改这个节点。如果上下文是int a,b;,那么a*b;就会被替换为乘法表达式。如果上下文是typedef int a;,那么a*b;保持不变(因为我默认是优先看成变量声明)。ReplaceStatement对于同一个statement只会调用一次。至于Pascal的集合操作也可以通过这个来完成。对于a+b,可以在ReplaceExpression里面查看a和b是不是集合类型,如果是的话替换成自己的PascalSetBinaryExpression。这个小技巧解决了语法分析的时候遇到的歧义问题。这也是没有办法的办法,因为这一次设计出来的结构的目的是为了让新的语言可以用很小的代价来实现。

    第二个做法是语法树的所有部分譬如Type、Expression、Statement和Declaration都存在一个ExtendedType、ExtendedExpression、ExtendedStatement和ExtendedDeclaration,语言可以通过继承这四个“扩展类”来提供未知的东西,当然这个时候就要连带提供所有操作了,譬如说根据语义分析的上下文来判断他自己的ExtendedExpression的返回类型啦。

    至于符号表的可扩展性,我设计了一个可以应付绝大多数情况的通用符号表,因此随时加入新的东西还是比较容易的。

    最新的代码可以在http://vlpp.codeplex.com/这里获得。
posted @ 2010-01-31 00:13 陈梓瀚(vczh) 阅读(2443) | 评论 (5)编辑 收藏
    开源了之后反而就不是很想写开发纪事了,因为每一次check-in到服务器上都会写几句描述,就有了一种已经公布了进度的错觉。Codeplex加上Visual Studio Team Foundation System真是方便,不像其他的源码管理系统一样要配置到半死,还要到命令行里面搞来搞去,好不容易找到一个UI插不进Visual Studio,好不容易找到一个能插的老是要挂。

    但是开源了之后就不好意思不写文档了。把自己的代码放上网的另一个好处就是总是觉得会有很多人来看,所以还是要写点文档介绍介绍。Codeplex是英文的,文档还不能只有中文。而且自己认识的很多程序员是说中文的,文档也不能只有英文。结果干脆就维护起了双语文档。不过双语文档真是麻烦啊。现在没看到什么文档生成工具可以产生多种语言(指自然语言)的文档的,而且格式还不好看(想象一下Doxygen的东西,全都是参差不齐的表格,不知道从哪里下手看啊),因为文档除了介绍每一个函数的作用以外,还要有编程指南。其实有些函数用指南来写反而比单独的介绍函数的返回值参数什么的要好得多。特别是C++。

    现在Vczh Library++3.0的Core Library应该算成型了,于是在做脚本引擎的部分。我打算把脚本引擎分为若干个模块。首先是基础语言的中间指令集,其实就像是专门为C语言设计出来的指令集。其次是基础语言的语法树,这次的语法树的类型规则要足够简单,因为主要是用来给高级语言的编译器产生输出的。第三是一门类似C#(当然这只是个比喻)的语言,然后把这个语言的语法树转换为C语言的语法树。想来想去之前 CMinus的失败其实并不是说高级语言语法树不能转换为C语言的语法树,而是设计出来的语法树太强大了反而不好拿来生成。最后就是动态语言了。

    这次做出来的应该是一个框架,我提供一大堆工具辅助开发语法分析器,然后底下有一层把虚拟机的事情全都给做了。于是就可以在上面用非常小的代价来实现各种各样的语言了。

    现在基础语言的中间指令集算是搞定了,当然只剩下一点点,不过这个在后面的东西完成之前没法继续写。C语言的编译器正在搞,我提供了一大堆的工具让人们可以在C++里面漂亮的拼出语法树,然后可以执行。当然以后还可能会有专门的语法分析器和代码生成器来在一些语言和这个语法树之间进行转换,不过这也仅仅是为了调试用的,因为要实现的语言其实是一门类似C#的高动态和静态结合的语言的语法树。

    但是开发一个语法树是相当麻烦的,因为语法树基本上就是几套大规模继承类的组合,然后要给他们写各种各样的虚函数,每一次要修改虚函数的参数表的时候都是大动干戈。于是今天早上开发出了一堆宏,用来方便地写语法树的visitor(这几乎是处理语法树的唯一办法),万一遇到需求变更,改起来更加方便。代码在Library\Scripting\Common\AlgorithmDeclaration.h,还附带有一个TestCase,在UnitTest\UnitTest\TestScripting_BasicLanguage.cpp里面。

    接下来就可以开发C语言语法树到中间指令集的转换了,然后一层一层做下去。最后要是有空,把最低下的中间指令集拿来一x86,整个瞬间就变成JIT了。
posted @ 2010-01-09 23:37 陈梓瀚(vczh) 阅读(2405) | 评论 (1)编辑 收藏

    最近在公司写了一大堆复杂的界面,终于体会到了前辈们那种上刀山下火海的感觉了。做完了之后回头想想,MVC还是有道理的。


    什么是MVC?其实可以简单的理解为一个有UI的程序可以划分为三个部分:数据层、逻辑层和应用层。当然这些名字是我乱起的。数据层顾名思义就是用来读写数据的地方,譬如说一个电话本的文件。逻辑层就是用户在界面上的操作的抽象,譬如说要通过名字来查找消息啦,给一个关键字求得筛选后的电话信息列表啦。应用层指的就是那一堆控件了。MVC三个字母分别指的是Model、View和Controller,也就是模型、视图和控制器了,分别对应于数据层、应用层和逻辑层。

    以前在看MVC的时候总是被一些教条主义的东西迷惑,说什么在MVC里面,MV解耦,所以M可被替换,V也可被替换。这个时候往往会感到迷惑。为什么模型,或者说数据层要被替换?为什么视图,或者说界面要被替换?其实这在一个不是复杂到神级级别的程序里面是不会发生的。但是MVC并不是为了让你能够实现模型被替换或者试图被替换而产生出来的,我觉得这个模式(其实这不是设计模式的其中一项,真的)更加重要的特点是可以让你的程序写起单元测试来更加容易。

    还是电话本,现在有一个要求,说在输入人的名字之后,只要系统检查出你超过0.5秒没有持续输入,那么底下的列表就会自动根据你上面的输入进行筛选。其实这有点像Outlook。这要怎么写单元测试?我们知道虽然正规的测试会有一大堆用来自动完成界面操作的工具啊,或者类库,但是作为单元测试来讲我们并不需要去做这种事情。因为单元测试是程序员写的,凡是程序员写的东西当然是需要尽快得到结果的。一般的开发方法是写一点代码,写一点测试,跑,有bug改没有bug继续。我们在开发程序的时候会不断地、频繁地跑单元测试,来看看我们的东西是不是有问题,或者在重构的时候我们对于我们的代码正确的信心会大一点。

    那界面怎么办呢?难道我们真的要去引入一个库来搞界面的自动测试吗?当然想要也可以,不过这毕竟太复杂,而且这一类的工具的稳定性其实都不是特别好,被误导的几率倒是大增。这仅仅是对于程序员来讲的,当然搞测试的那些人自有他们的办法。那既然我们不做界面的自动测试那怎么知道文本框被输入之后究竟筛选出来的数据对还是不对呢?

    答案:MVC。

    为什么View,也就是试图,也就是界面,可以被替换是一件很重要的事情?想一想,如果控件可以被换成单元测试的一段代码,那岂不是很爽么?举个例子,我们要告知用户说,我们的事情已经做了一半了,这个时候我们可能会去设置进度条的位置。但是“告诉用户说我们的事情已经做了一半”跟“设置进度条的位置”其实是完全无关的两件事情。因此我们的Controller要负责通知View说事情做了一半了,然后View就可以去设置进度条的位置了。现在我们把View换成单元测试的一段代码,这个时候就变成Controller通知测试程序说事情已经做到一半了,然后测试程序就会去检查说现在是不是应该做到了一半,如果应该,显然这个用例就通过了。

    那Model呢?Model可以简单的理解为数据源,其实当然不只是那么简单,不过这样理解会让我们更容易接受一点。数据源是什么,当你写单元测试的时候,去连接一个数据库来获得数据源,然后就操作Controller,这个时候你如果不亲自去读一下数据库,你怎么知道Controller给你的东西究竟是对的还是错的?显然Model我们也可以换掉,测试程序伪造数据成为一个Model,然后插入Controller,事情就解决了。数据是我们自己给的,那Controller应该提供什么我们也能知道了。

    于是,使用了MVC之后,单元测试想换Model就换Model,想换View就换View,测试什么就非常容易了。至于说用户停止输入0.5秒之后是不是会真的去进行数据的筛选,这个我们手工测试就好了,而且那些搞测试的人也会帮我们检查的。

    好吧,说到这里有人可能会问为什么我没有给出一个Demo?这东西太虚,实践实践自己体会一下就行了,而且MVC变形那么多,有Model-View-Presenter,还有最近兴起的Model-View-ViewModel等等,其实现都跟传说中的那个类似桥接模式的东西差别甚远。这个自己去看一看就好了。
posted @ 2010-01-08 03:58 陈梓瀚(vczh) 阅读(5058) | 评论 (9)编辑 收藏
zero = \s.\z.z
succ = \a.\s.\z.a s (s z)
add = \a.\b.a succ b
mul = \a.\b.a (add b) zero
pow = \a.\b.a (mul b) (succ zero)

true = \a.\b.a
false = \a.\b.b
if = \c.\t.\f.c t f
not = \a.a false true
and = \a.\b.a b false
or = \a.\b.a true b
xor = \a.\b.a (not b) b

pair = \a.\b.\c.c a b
fst = \p.p true
snd = \p.p false
const = true

iszero = \n.n (const false) true
pred = (\pack.\packpred.\n.snd (n packpred (pack n))) (\n.pair zero n) (\n.iszero (fst n) (pair (succ zero) (snd n)) (pair (succ zero) (pred (snd n)))
sub = \a.\b.b pred a

eq = \a.\b.and (iszero (sub a b)) (iszero (sub b a))
ne=\a.\b.not (eq a b)
lt = \a.\b.and (iszero (sub a b)) (not (iszero (sub b a)))
le = \a.\b.or (eq a b) (lt a b)
gt = \a.\b.not (le a b)
ge = \a.\b.not (lt a b)

div = (\init.\step.\a.\b.add (fst (a step (init a b))) (fst (b step (init b a)))) (\a.\b.pair zero (pair a b)) (\i.(ge fst(snd i)) (snd (snd i))) (pair (succ (fst i)) (sub (fst (snd i)) (snd (snd i))) (snd (snd i))) i)

empty = pair zero zero
cons = \a.\b.pair (succ zero) (pair a b)
isempty = \l.iszero (fst l)
head = \l.fst (snd l)
tail = \l.snd (snd l)

Y=\f.(\t.f (t t))(\t.f (t t))
foldr = Y (\self.\f.\e.\l.(isempty l) e (f (head l) (self f e (tail l))))
length = \l.foldr (true succ) zero l
sum = \l.foldr add zero l
product - \l.foldr mul (succ zero) l
append = \h.\l.foldr cons l h
reverse = \l.foldr (\a.\b.append b (const a empty)) empty l
map = \f.\l.foldr (\a.\b.cons (f a) b) l
zip = Y (\self.\a.\b.(or (isempty a) (isempty b)) empty (cons (pair (head a) (head b)) (self (tail a) (tail b))))
flatten = \l.map append l
posted @ 2009-12-24 08:55 陈梓瀚(vczh) 阅读(2475) | 评论 (4)编辑 收藏
    今年事情比较多,第一个是自己终于从本科毕业了,第二个是自己找到了工作,拿了一份offer。虽然当初为了去Microsoft实习错过了一大堆其他公司的面试机会,只投了Microsoft,Google和百度。不过最后还是进了Microsoft,在这危机四伏的日子里,虽然说自己写了10年代码总归有点功力,但是也有运气的成分在。只是面试百度的时候我明明在简历上写了我人在上海,他非要我飞回广州面试,很不爽,拒了他。

    去年下半年几乎都投身Microsoft的实习,到了12月上旬回学校,于是从今年的元旦开始其实就是在学校里面混日子了。其实还好,完成了一个阉割版的Haskell编译器当毕业设计,还做了一个C语言编译到机器码写入内存的编译器,最后重写了Vczh Library++3.0,还把它开源了,虽然还没最终完成。不过开源了之后就得补文档了,因此近期的开发进度可能会慢一点。

    回想大学四年,还是写了不少代码的,也足够拼成一张至少可以留住HR眼球的简历了。当初比较大的转变是我刚上大一,可怜的Borland就不行了,于是很喜欢的Delphi看来也有危机了,所以转去C++。幸好所学的知识并不是绑定在Delphi的平台上,因此刚开始也只觉得是换了个语法。不够C++实在是博大精深,里面可以用各种各样的范式写代码,比较突出的是元编程,虽然这种东西在现实生活中重要性不言而喻不过所占比例还是很小的。

    于是开始拿C++练手了。高中的Delphi时代写了不少游戏,积累了一个2D的游戏引擎,其实也不复杂,不过好歹也到了3.0了,里面有图形图像、音效、数据管理、脚本引擎,还有一个UI。因此C++上手了之后,自然是移植它了。移植的过程中发现C++的写法跟Delphi还是截然不同,因此Vczh Library++ 1.0基本上是失败告终,虽然那个Delphi的游戏引擎大部分都实现了。在开发的途中我曾经写了一个模仿显卡固顶管线的3D软件渲染器,不过最后一个Demo应该是在大二,用OpenGL实现了3D模型的骨骼动画,用的好像还是Halflife 1的几个模型,什么鸟啊,僵尸啊,警察啊。后来觉得实在是找不到美工,而且自己还有一项喜欢的,也就是写编译器了,所以干脆就集中力量搞编译器吧。

    第一个见得人的编译器应该是Vczh Jove Script了。这个东西阉割了Java,然后实现了一次,主要是针对OOP,有继承,有虚函数,还有泛型。当然泛型我实现了跟C#一样的参数约束,也就是可以指定说某个类型参数必须继承与另一个类。数组使用引用计数,其他的都垃圾收集。当然最后发现数组用引用计数是不对的,会导致垃圾收集。

    之后我就对计算机的理论燃起了热情了,首当其冲当然是编译原理。当时受到了CSDN上那个袁泳的一点指导,其实主要不是技术上的,是方向上的,后来给我看了一本很厉害的书叫《Parsing Techniques》。很多知识都从这里面吸收了,然后就要开刀,当然是从最简单的正则表达式引擎下手。第一次写还是有点别扭,到现在一共写了三次,其中第二次是在第一次写完了之后觉得很不爽立刻重写的。写完了就轮到Syngram,是一个将文法写进C++然后自动变成语法分析器的小库。当然后来也重写了。

    上面的事情完成了之后就着手Vczh Free Script的开发了。这是一个“纯”动态语言。为什么说纯呢,因为我坚持所有东西匿名(包括类定义,其实结果就是返回一个类型,像C#的System.Type,然后可以到处传),所以为了给一个东西命名就写一个赋值语句。当然不仅如此,我还实现了函数闭包,然后将之后的所有特性譬如说动态的Multiple Dispatch(虚函数是Single Dispatch)啊,namespace啊,类和继承什么的统统编译到函数闭包上,整个语言是匿名的。当然我还是把它是实现成一个C++的类库,如果你愿意在我的接口下面写插件的话,就可以跟Python一样直接应用到你自己的工程里面去了。

    在这个过程中我学习了很多关于编程语言方面的基础理论,还学了一点数学虽然我还是觉得数学有点难度。完了之后就开发一个小型的IDE,其亮点是就算代码是动态生成的,我也能捕捉到然后给你单步调试。不过这个由于稳定性并不是非常好,第一次将C++跟C#混起来用还是有点力不从心,因此就没拿出来贡献给大家了。

    之后就开始Microsoft的实习之旅了,在实习的过程中我首先封装了一次win32api的GUI部分,尽量达到跟Delphi一样好用,于是有了这个Demo,然后做了阉割版Haskell——也就是Kernel FP了。当初叫这个名称我只是想看看实现一个最小的纯函数式语言的内核要怎么办,要包含多少功能(当然是越少越好,其他的都是语法糖或者库,不过不能让能力下降)。后来又看了一本书好像叫做《The Implementation of Functional Programmang Languages》,也很好看,学到了很多东西。

    于是2008年就结束了,进入2009年,做了一个CMinus,可以把C语言编译到内存里面,搞成x86的机器码,然后就能将一个写了代码的txt文件变成一个函数指针了。然后就毕业了。

    7月13日开始入职Microsoft,虽然说是在开发界面,不过我还是觉得需要自己仍然保持热情,于是工作结束之后自己要继续写自己的代码,也就是Vczh Library++ 3.0了。上面做了很多4个编译器,刚好针对语言的4中特性,这次看看能不能把它们综合起来,变成一个真正有用的脚本引擎。当然这不是重复劳动了,毕竟自己实现给自己带来的质的提高会比你纯粹用别人的要高很多。但至于最后怎么办,其实我还是觉得.NET的潜力比较大,总之挑战它是不明智的,但我还是想自己试一试。

    从第一个QBasic的Hello World到现在也差不多要10年了,初中因为不小心拿到了本QBasic的书然后戏剧性地开始了我程序员的人生,所幸中间没有间断过,而且也将对一贯来编程的激情很好的保存了下来,有增无减。至于说30岁(其实日本说的是35岁)就要转管理什么的,我还是不太相信,或者说我愿意就做一线的开发人员,或者架构师(当然这个跟通常意义上的架构师还是不一样的,有朝一日真的给我做了,我还是想跟一线的程序员一起写代码)。管理还是不适合我,毕竟我对钱(或者是权力?)没那么渴望,够花就好了,虽然我自己没多少钱。

    总之,要有激情,无论是对什么事情。剩下的就是要追求快乐,不同的人对快乐的定义还是不一样的,不过我目前只要能写有挑战性的代码,我就会觉得很快乐了。工作了之后因为在上海,瞬间感到了房价的压力。只是如果要我牺牲写代码的时间和乐趣去换取那些所谓的财产,我还是不太愿意的。
posted @ 2009-12-23 05:22 陈梓瀚(vczh) 阅读(14764) | 评论 (40)编辑 收藏
    项目主页:http://vlpp.codeplex.com/

    Vczh Library++从2006年就开始开发,到现在经历了一些版本变迁,到现在已经正式步入3.0了。现在Vczh Library++ 3.0的基础部分已经成型,我的目标是将Vczh Library++ 3.0做成一个在性能不是极端苛刻情况下使用的数据处理库,附带一个高速的脚本引擎。未来可能会提供更多的东西,但主要围绕着这两个目标走。

    我选择CodePlex主要是因为CodePlex支持Team Foundation System,这个系统跟Visual Studio 2008结合的相当好,提交更改都非常方便。而且CodePlex也提供SVN服务,一些不喜欢IDE的大大们也可以用SVN来下载代码。

    至于为什么上面的描述是英文的,因为在美帝公司习惯了……
posted @ 2009-12-13 03:21 陈梓瀚(vczh) 阅读(4793) | 评论 (18)编辑 收藏
仅列出标题
共35页: First 12 13 14 15 16 17 18 19 20 Last