設計一種面向對象腳本語言

有沒有感覺設計一門語言實在是太有意思了,可以自定義語法規則,我的“地盤聽我的”。

腳本語言的功能

本書設計一門純粹的面向對象腳本語言,任何語言都有個名詞,這裏給這個語言起個名字——sparrow(麻雀)。它支持的功能如下。

1 變量

支持局部變量和局部變量的定義。

變量可引用、賦值。

內部複合數據類型以大寫字符開頭,如System.print()

2 基本數據類型

數值:包括整數和浮點數。

字符串:包括普通字符和unicode。

list:列表,如Python中的list。

支持字面量創建,如['a', 'b']和new方法創建。

元素通過下標list'[索引]'獲得。

map:哈希數組,如Python中的字典。支持字面量創建和new創建。字面量創建如:

{

    'k1':v1
    "k2":v2
}

key可以是任何數據類型。同樣支持new方法創建。

value通過下標map'[key]'獲得。

range:用以確定一段整數範圍,用符號..表示。range包括from和to兩個成員,分別表示這段範圍的起和始,用區間表示[from, to],即包括from和to。如range“2..6”,2就是from,6就是to,“2..6”表示2、3、4、5、6。“..”類似於Python中的分片操作符“:”,只不過我們包括了結尾的to,而Python不包括,若用區間表示則to後面的是右小括號“)”。

3 運算

數值:+、−、*、/、%。

邏輯:>、<、==、!=、||、&&、?:、|、&等。

位運算:>>、<<。

方法調用:.。

索引:[]。

字符串:+、%(字符串內的表達式)

4 控制結構

支持if-else選擇。

支持while循環。

支持for循環。

支持break退出循環。

支持continue,跳過本次循環體後面的部分,繼續下一輪循環。

支持return返回。

5 函數

儘管這是一門面嚮對象語言,但也支持傳統意義上的函數,用關鍵字fun實現函數定義。

函數也是用類實現。

支持函數重載。

6 類

就是傳統意義上的class,包括類定義和類實例,靜態類。

實現繼承,所有類都是object類的子類。

類成員(也稱域,或字段)必須先聲明再引用。

方法包括method、getter、setter、subscript、subscriptSetter和構造函數。支持塊參數,塊參數的參數是用“||”括起來的參數列表,以逗號分隔。

支持靜態方法。

7 線程

支持線程創建及調度。

8 模塊

支持執行模塊和模塊內模塊變量的單獨導入。

9 註釋

行註釋://

塊註釋:/* 塊註釋 */

以上列舉若有遺漏則以實際代碼爲主。

關鍵字

有以下關鍵字被提前徵用了。

var:用於變量定義。

fun:用於函數定義。

if:用於條件判斷。

else:用於條件判斷的else分支。

true:bool值真。

false:bool值假。

while:用於while循環。

for:用於for循環。

break:用於退出循環。

continue:用於結束本次循環並進入下一輪循環。

return:用於從函數返回。

null:空值。

class:用於類定義。

is:用於判斷類是否爲某類的子類,即“is a”。

static:用於設置靜態文法。

this:用於指向本實例。

super:用於指向父類。

import:用於導入模塊。

腳本的執行方式

我們採用傳統的虛擬機作爲執行方式,即要實現一個虛擬機。編譯器先把源碼編譯爲opcode,再讓虛擬機執行opcode。

opcode即操作碼,是自定義的一套專供虛擬機執行的指令,後面我們在實現虛擬機時會詳細介紹。 

“純手工”的開發環境

既然本着教學的目的我覺得應該拿出教學的誠意,因此這裏所說的“純手工”是指編碼中不想借助STL或其他類似的泛型語言,沒有第三方庫,一切以最基礎最原始的形式展現語言的奧祕,因此選擇了C語言,確定地說是C89,並不是較新的C99標準,誠意滿滿,讓我們純手工去編碼吧。

基礎開發環境是:

宿主系統是Linux,採用CentOS release 6.8 (Final);

編譯器是gcc,版本是gcc version 4.4.7 20120313 (Red Hat 4.4.7-17) (GCC)。

定義sparrow語言的文法

在之前介紹的文法中,我們採用的是大寫字母表示非終結符,小寫字母表示終結符,然而我們也說過了,在現實中爲了便於編程,一般都用正規文法來定義語言,正規文法說白了就是用正則表達式定義的文法,因此本小節的基礎是正則表達式,爲保證容易看懂,我會用最簡單的方式書寫正則表達式。

值得注意的是,正規文法與第0章中介紹的文法有很大的差別,主要是涉及終結符是用''表示,原因是正規文法中會涉及()、[]和{},這些在正規文法中都是元字符,有其特殊含義:()表示成爲一組,[]表示範圍,{}表示重複。

但在實際語言它們只是字符串字面量(即終結符),比如在語言中()表示函數名後面的括號,也可表示表達式中的小括號,[]表示下標索引,因此爲避免衝突,正規文法中用單引號括起的是終結符。

其實語法和傳統語言差不多,只是用文法來描述就顯得生澀了。注意,正規文法中的[]與EBNF中的意義不同,在此表示範圍,其中可以用-表示一段連續的範圍,比如0-9就表示0至9之間(包括0和9)的任何數;a-z同理,表示字母a到字母z之間的任何字母。[]後面一般會接量詞,當然量詞不一定只用在[]之後,但它一定是用在某個字符之後以表示該字符的數量,其前不能沒有字符。

按照數量級別劃分有3種量詞,+表示重複出現1次以上,*表示重複出現0次以上,?表示出現0次或1次,比如可用[\t]*表示0個或多個空白字符,其中\t是tab。

注意此處[]中的空白是空格,爲了突顯這裏有個空格就寫了兩個。.表示任意字符,包括控制字符比如回車等,|表示或者、任意其一,比如a|b,表示a和b兩者取其一,注意,|是對兩邊的整體有效,並不是只對緊鄰|的有效。

比如對於ab|cd的意思是ab或者cd,如果想表達abd或acd,可以用分組符號(),就是小括號對兒。()表示作爲一組考慮,使相應的正則符號應用於整個組成員。

初次接觸文法的讀者可能對遞歸定義感到“消化不良”,比如非終結符exp是用於定義表達式,exp是由infixExp等非終結符組成,而infixExp又是由exp組成,看上去有點死循環出不來了,但你不要忘了,infixExp只是exp的其中一個組成部分,exp還可以由num、id等指代,num和id下面再無遞歸定義,這就是遞歸終止的條件。因此infixExp的組成部分exp也會是num或id等。

下面是具體的樣本。

以下是上述文件的執行結果,其中的spr是最終的腳本解釋器(包括編譯器及虛擬機),spr是sparrow的縮寫。


以上./spr manager.sp就是執行腳本文件manager.sp,這與任何腳本語言的運行方法都是一致的,執行過就是腳本的輸出,大家有興趣可以覈對一下結果,除了System.clock返回的時間戳是動態變化的外,其他不變。

《自制編程語言》

鄭鋼  著

本書全面從腳本語言和虛擬機介紹開始,講解了詞法分析的實現、一些底層數據結構的實現、符號表及類的結構符號表,常量存儲,局部變量,模塊變量,方法存儲、虛擬機原理、運行時棧實現、編譯的實現、語法分析和語法制導自頂向下算符優先構造規則、調試、查看指令流、查看運行時棧、給類添加更多的方法、垃圾回收實現、添加命令行支持命令行接口。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章