1. 程式人生 > >設計一種面向物件指令碼語言

設計一種面向物件指令碼語言

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

指令碼語言的功能

本書設計一門純粹的面向物件指令碼語言,任何語言都有個名詞,這裡給這個語言起個名字——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返回的時間戳是動態變化的外,其他不變。

鄭鋼  著

本書全面從指令碼語言和虛擬機器介紹開始,講解了詞法分析的實現、一些底層資料結構的實現、符號表及類的結構符號表,常量儲存,區域性變數,模組變數,方法儲存、虛擬機器原理、執行時棧實現、編譯的實現、語法分析和語法制導自頂向下算符優先構造規則、除錯、檢視指令流、檢視執行時棧、給類新增更多的方法、垃圾回收實現、新增命令列支援命令列介面。

小福利

你認為程式設計師存在的最大價值是什麼,參與話題有機會獲獎哦。截止時間9月7日17時,留言+轉發本活動到朋友圈,小編將抽獎選出3名讀者贈送紙書1本

掃碼關注我們

點選閱讀原文,直接購買《自制程式語言》

閱讀原文​​​​