摘要: 做編譯器難免會遇到如何輸出錯誤信息的問題。這個問題不是那么好解決的,因為有可能你需要提供中文、英文還是其他什么亂七八糟語言的編譯器。于是就要想一種辦法讓錯誤信息的語言可以很方便的切換。 由于同時提供中文和英文的編譯器其實沒什么用,中文的編譯器和英文的編譯器其實可以是兩個不同的exe,因此我想寫兩個宏,譬如說中文.bat點擊了...
閱讀全文
posted @
2010-03-05 23:08 陳梓瀚(vczh) 閱讀(2505) |
評論 (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) 閱讀(2410) |
評論 (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) 閱讀(2719) |
評論 (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) 閱讀(5236) |
評論 (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) 閱讀(2461) |
評論 (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) 閱讀(2411) |
評論 (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) 閱讀(5069) |
評論 (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) 閱讀(2489) |
評論 (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) 閱讀(14821) |
評論 (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) 閱讀(4811) |
評論 (18) |
編輯 收藏