前言
目前很有野心,打算做個大事。雖然程式設計水準還不高,看《編譯原理》也看不下去,看視訊教程看了十幾節課發現筆記上全是數學符号。就——暫時不看了吧!我之前因為好奇買了本《兩周自制腳本語言》,的确學了一些知識。但是其中從第五天開始,都使用了一個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檔案的路徑,并運作解釋器。
- 從 .suatin檔案中讀取字元串,一行行地組成一個字元串,每讀取一行就在後面添加一個\n——也可以添加\r\n,那樣的話稍微有點不同而已
- 對整個字元串進行正則比對,把關鍵字、辨別符、五則運算符(+ - * / ^ )、判斷運算符(> < <= >= == ~=)、小括号、中括号、大括号、數字(含小數、指數)、注釋(含單行注釋、多行注釋)、換行\n、分号、等于号、逗号運算符都分割出來,然後得到分組索引,映射為優先級後,除了注釋外的符号都建立Token。
- 把Token放入全局中綴表達式中。
- 周遊中綴表達式,分割語句,确定辨別符(即變量、函數)作用域,把作用域資訊儲存到Parser和SuaObject上。分類語句,把一條條的語句放入語句鍊中——放語句在中綴表達式中的範圍就行——執行代碼就是執行這條語句鍊!
- 根據代碼中的變量定義語句,建立SuaObject對象的執行個體,并把指針放入Pool池子中。
- 構造文法樹AST。
- 求解文法樹
因為是直接運作的,沒有中間代碼,是以與其說是解釋器,不如說是直譯器!
項目現在做到了那個位置?
下面是測試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,操作更友善哦