天天看點

【Suatin】不學編譯原理就制作語言--------談談自制程式設計語言1

前言

目前很有野心,打算做個大事。雖然程式設計水準還不高,看《編譯原理》也看不下去,看視訊教程看了十幾節課發現筆記上全是數學符号。就——暫時不看了吧!我之前因為好奇買了本《兩周自制腳本語言》,的确學了一些知識。但是其中從第五天開始,都使用了一個Parser庫,看知乎知道了難點不在這個庫上,是以一般都用現成的。而書的問題是代碼沒多少注釋!!!實在搞不明白,再加上java用起來實在不爽!于是這條路也抛棄了,最後我打算先從一個中綴表達式轉字尾表達式搞起——因為機器識别不了式子,隻能識别符号和數字,是以可以先轉換成字尾表達式,然後通過這個表達式求解。

一般在文法分析的時候,需要構造AST文法樹。而中字尾這條路不用,需要利用棧。

形成文法樹可以把各種符号涵蓋進去,而中字尾隻能計算數字直接的加、減、乘、除、乘方等簡單的一進制或二進制操作,最後也隻能得到一個計算結果(一般是double類型的數)。

我覺得中字尾這條路挺好用的,凡是表達式都可以用這種方法得到結果,如果有函數在其中,就把函數替換為函數的傳回結果!

雖然這麼說!但是這隻是對表達式而言!對于其他語句和語句塊,還是要構造AST的!

我目前的項目進度到了分割語句和分類語句上,已經完成了分割Token和分類Token,因為項目中變化太多,加上現在已經有了31個檔案,實在沒辦法放上來。但是可以把一些概念貼上!

上層語言與下層語言的關系

上層語言是我的自制語言Suatin-lang,下層語言是C++。

上層語言方法都是小寫,下層語言方法大部分都是駝峰

上層語言的類型,如int/number/BigNumber/complex/string/bool/function/array/file都繼承于object,不是因為我想搞面向對象和高階函數什麼的,而是這樣後面建立對象和傳遞對象時比較友善!!!

下層語言C++中有對應的類,如SuaInt/SuaNumber/SuaBigNumber/SuaComplex/SuaString/SuaFunction/SuaBool/SuaArray/SuaFile/SuaObject,這些類專門對應着處理上層語言的資料對象——變量、函數等。

//SuaUtil.h
class SuaObject {
protected:
	bool isconst = false;///是否是常量。const可以修飾SuaObject
	std::string name="";///辨別符名稱
	int zoneIndex;//作用域索引,和pos一起用的。隻有SuaObject才有這個

public:
	//傳回類型名的字元串,因為源碼是字元串,輸入輸出都是字元串處理
	virtual std::string getname()const = 0;

	//傳回類型
	virtual std::string gettype()const = 0;



	///常量操作
	bool is_constant()const;
	void setconst();
	void resetconst();

};

           

解釋器中的類

下層語言還有其他的類,如Token。源碼中所有的符号被分類,每個符号或辨別符都放入一個Token,所有的Token都會放入全局的容器中,即放入全局中綴表達式中。特殊的Token會包含SuaObject對象——即自定義變量或函數。

Expr——隻有四則加乘方的運算才能算作表達式,這個我還沒認真修改。隻有個空殼。表達式裡關鍵是兩個變量,start&end,表示表達式在全局中綴表達式的中範圍。

Pool——存儲池,自定義的變量或函數,即SuaObject對象,會放入這個池子裡。暫時不想搞類型推導,是以暫時打算做靜态類型的語言。每次遇到一個辨別符都會和池子裡的進行比對,看看池子裡是否有。檢查辨別符靠的是辨別符的名稱,不是位址。

在上層語言中,一個變量或函數隻有兩個關鍵的點,一是它的值,放在底層的SuaObject中。二是它字元串的名稱,這意味着在此上層語言中,不能對關鍵字建立變量!!!

Parser——用來周遊中綴表達式,分割語句和分類語句。其中有幾個關鍵變量:start&end&三個作用域計數器&作用域容器。

  • start&end從頭開始周遊到尾,來分割不同的語句。
  • 作用域計數器就厲害了。每遇到一個左括号就加一,遇到右括号就減一,即當計數為n時的位置是A時,再到了計數為n-1的位置為B後,A-B之間就是一個作用域!!!識别不同作用域靠n的值。
  • 三個作用域計數器就是三個int,分别計數小括号、中括号、大括号。
  • 作用域容器放一個結構體,用來表示一個作用域。内容是作用域索引、作用域開頭、作用域結尾。每個SuaObject記憶體放一個自己的作用域索引,Token中有該符号的位置,把位置和索引都傳入Parser的作用域檢測方法,就能知道此變量是否在該作用域内
  • 但是當兩個作用域并行時,他們的索引是一樣的,是以需要再引入一個變量number。區分并行的作用域!
//Parser.h
//這個類暫時的樣子
class Parser
{
private:
	//一個語句的起始就是start到end-1
	int start;//語句開頭,可以是辨別符、數字
	int end;//語句結尾,可以是\ n   ;   }    {
	//一個作用域的起始就是count_zoneQ_xxx=n到count_zoneQ_xxx=n-1時,對應的兩個位置之間
	//下面三個計數器都是遇到左括号加一,遇到右括号減一。不同的作用域間的數不同。這個數指的就是作用域索引。
	static int count_zoneQ_little;//小括号作用域計數
	static int count_zoneQ_middle;//中括号作用域計數
	static int count_zoneQ_big;//大括号作用域計數
	

	//花括号作用域索引,在使用語句前,先經過檢查是否在對應的作用域内—— GetVal()
	//索引小的作用域大!!!是以如果是作用域1的數,可以在作用域2/3内使用
	typedef struct _ZONE{
		int index;//作用域索引
		int number;//區分并行作用域
		int start;//作用域開頭
		int end;//作用域結尾
	}ZONE;
	static std::vector<ZONE*> zone;


public:
	
	//分割語句
	static void SyntaxSeparate();
	//分類語句
	static void SyntaxClassify();

	//檢查變量的位置是否在其作用域内
	static bool InZone(int index,int number,int pos);
	

};
           

shell——解釋器的互動面。從檔案中讀入字元串、Token的分割與分類、顯示Pool池子的内容、顯示中綴表達式的内容、顯示語句分類的内容等等直接得到處理效果或是運作效果的函數。

SuaUtil——最底層的倉庫。自定義異常處理、各種枚舉、基類的定義等等。既然是最底層的類,那麼絕對不能放太多的東西來增加依賴,應該不依賴其他自定義的檔案!

SuaMath——一些數學操作的集合。

SuaExcept——自定義異常類,枚舉了一些能想到的錯誤。具體請見之前的文章lang:C++自定義異常類——用來處理自制程式設計語言的異常資訊。

檔案的引用關系

項目大了要分好檔案,不能有交叉引用。因為如果後續我使用模闆、友元的話會很麻煩。

【Suatin】不學編譯原理就制作語言--------談談自制程式設計語言1

解釋器的結構

目前腦子裡的解釋器是下面這樣,以後完成了肯定會不同的。

解釋器運作流程

  1. 傳入 .suatin檔案的路徑,并運作解釋器。
  2. 從 .suatin檔案中讀取字元串,一行行地組成一個字元串,每讀取一行就在後面添加一個\n——也可以添加\r\n,那樣的話稍微有點不同而已
  3. 對整個字元串進行正則比對,把關鍵字、辨別符、五則運算符(+ - * / ^ )、判斷運算符(> < <= >= == ~=)、小括号、中括号、大括号、數字(含小數、指數)、注釋(含單行注釋、多行注釋)、換行\n、分号、等于号、逗号運算符都分割出來,然後得到分組索引,映射為優先級後,除了注釋外的符号都建立Token。
  4. 把Token放入全局中綴表達式中。
  5. 周遊中綴表達式,分割語句,确定辨別符(即變量、函數)作用域,把作用域資訊儲存到Parser和SuaObject上。分類語句,把一條條的語句放入語句鍊中——放語句在中綴表達式中的範圍就行——執行代碼就是執行這條語句鍊!
  6. 根據代碼中的變量定義語句,建立SuaObject對象的執行個體,并把指針放入Pool池子中。
  7. 構造文法樹AST。
  8. 求解文法樹
【Suatin】不學編譯原理就制作語言--------談談自制程式設計語言1

因為是直接運作的,沒有中間代碼,是以與其說是解釋器,不如說是直譯器!

項目現在做到了那個位置?

下面是測試Suatin-lang的源碼,C++項目就省略了。

//test.suatin
int sum=0.0e0
const int i=0
string str="hello world suatin-l
	ang programmer!"
if str == null 
{ print("yes") }
else
{
	i=i+1      //zhushi
	sum=sum+i /*duohangzhushi
         */
	sum=sum/2;sum=sum+sum*i
}
print(sum+i)//zhushi
           

顯示結果:

welcome to suatin interpretor interface

C:\Users\LX\Desktop\test.suatin内代碼為>
int sum=0.0e0
const int i=0
string str="hello world suatin-l
        ang programmer!"
if str == null
{ print("yes") }
else
{
        i=i+1      //zhushi
        sum=sum+i /*duohangzhushi
         */
        sum=sum/2;sum=sum+sum*i
}
print(sum+i)//zhushi
--------------代碼結束!--------------
中綴表達式>
           name     pos  priority
            int      0       1
            sum      1      12
              =      2       8
          0.0e0      3      18
             \n      4       6
          const      5       1
            int      6       1
              i      7      12
              =      8       8
              0      9      18
             \n     10       6
         string     11       1
            str     12      12
              =     13       8
        "hello world suatin-l
        ang programmer!"     14       5
             \n     15       6
             if     16       1
            str     17      12
             ==     18      11
           null     19       1
             \n     20       6
              {     21       3
          print     22      12
              (     23      10
          "yes"     24       5
              )     25      10
              }     26       4
             \n     27       6
           else     28       1
             \n     29       6
              {     30       3
             \n     31       6
              i     32      12
              =     33       8
              i     34      12
              +     35      15
              1     36      18
             \n     37       6
            sum     38      12
              =     39       8
            sum     40      12
              +     41      15
              i     42      12
             \n     43       6
            sum     44      12
              =     45       8
            sum     46      12
              /     47      14
              2     48      18
              ;     49       7
            sum     50      12
              =     51       8
            sum     52      12
              +     53      15
            sum     54      12
              *     55      14
              i     56      12
             \n     57       6
              }     58       4
             \n     59       6
          print     60      12
              (     61      10
            sum     62      12
              +     63      15
              i     64      12
              )     65      10
             \n     66       6
第1條語句>sum=0.0e0
第2條語句>i=0
第3條語句>str="hello world suatin-l
        ang programmer!"
第4條語句>str==null
第5條語句>print("yes")
第6條語句>i=i+1
第7條語句>sum=sum+i
第8條語句>sum=sum/2
第9條語句>sum=sum+sum*i
第10條語句>print(sum+i)
作用域資訊>
index = 1,number=1,start=21,end=26
index = 1,number=2,start=30,end=58
請按任意鍵繼續. . .
           

CSDN

項目在我的資源裡(要多少C币不是我決定的,明明設定的需要0C币,但是之後看被改了555)!

項目代碼位址BDWP

連結:https://pan.baidu.com/s/1yX7j69kpbr4a5kd-V-LyRg

提取碼:2agq

複制這段内容後打開百度網盤手機App,操作更友善哦

繼續閱讀