自制作業系統Antz(14)——實現內建程式語言(詞法解析器)
AntzScript
:mortar_board: The language executed in the Antz system.
基於以實現的內建vim,接下來需要實現Antz系統中內建語言AntzScript。
vim |
---|
![]() |
一、概述
1. 直譯器與編譯器
1.1 直譯器
直譯器根據程式中的演算法執行運算。簡單地說,它是一種用於執行程式的軟體。
1.2 編譯器
編譯器能將某種語言寫成的程式轉換為另一種語言的程式。通常它會將源程式轉換為機器語言程式。編譯器轉換程式的行為稱為編譯,轉換前的程式稱為源程式。
- Java虛擬機器為了提高效能,會在執行過程中通過編譯器將一部分Java二進位制程式碼直接轉換為機器語言使用。
過去的編譯器編譯過程非常費時,不過由於編譯後實際執行的是機器語言,因此執行速度很快。而直譯器,會在程式輸入的同時立即執行,執行速度較慢。這就是兩者的基本區別。
在為Antz製作內建指令碼語言時,因為是直接執行,處理器屬於直譯器,不過,為了提高效能,該直譯器內部將採用編譯器。
基本流程如下
![]() |
![]() |
2. Token
語言處理器的第一個組成部分是詞法分析器(lexer)。程式的原始碼最初只是一長串字串,這樣的字串很難處理,語言處理器通常會首先將字串中的字元以解析單元分組,切割成多個Token,這就是詞法分析。
解析結果 |
---|
![]() |
![]() |
Token類中需要對原始碼分割成 最小單元
,並解析出 變數型別
, 數值量
, 符號
, 識別符號
等
Token |
---|
![]() |
Token中k代表單元含義,lexeme代表其在程式碼中的內容。kindMap是一個提前預置關係轉換符表。在需要新增其他關鍵字時可以直接在KindMap中新增。 |
![]() |
詞法解析器的實現方法有手工構造法和正則表示式的方法。
手工構造法-狀態圖 |
---|
![]() |
這種方法較為複雜,需要大量的程式碼,而正則表示式的方法較為便捷。
二、實現詞法解析
藉助正則表示式庫能簡單地實現詞法分析器。下表列出的記號在大多數情況下都能使用。例如, .*\.java
指的是以 .java
結束的任意長度的字串模式。 .*\.
由兩部分組成, .*
表示由任意字元組成的任意長度的字串模式, \.
表示與句點字元相匹配的字串模式。 (java|javax)\..*
則表示由 java.
或 javax.
起始的任意長度的字串模式。
元字元 | 含義 |
---|---|
. | 與任意字元匹配 |
[0-9] | 與0至9中的某個數字匹配 |
[^0-9] | 與0至9這些數字之外的的某個字元匹配 |
pattern* | 模式pattern至少重複出現0次 |
pattern+ | 模式pattern至少重複出現1次 |
pattern? | 模式pattern出現0次或1次 |
pattern1 | pattern2 |
() | 將括號內視為一個完整的模式 |
\c | 與單個字元c(元字元*或.等)匹配 |
整型字面量的匹配
[0-9]+
識別符號的匹配
[A-Z_a-z][A-Z_a-z0-9]*
- 以字母、下劃線開頭,之後僅包含有字母、數字、下劃線的就是識別符號的定義規則。
[A-Z_a-z][A-Z_a-z0-9]*|==|<=|>=|&&|\|\||\p{Punct}
最後的 \p{Punct}
表示與任意一個符號字元匹配。模式 \|\|
將會匹配 ||
。由於|是正則表示式的元字元,因此在使用時必須新增反斜線轉義。
最後需要定義的是字串字面量。由於不得不處理各種轉義字元,字串字面量的定義略微複雜。
"(\\"|\\\\|\\n|[^"])*"
首先,從整體上看,這是一個 "(pattern)*"
形式的模式,即雙引號內是一個與pattern重複出現至少0次的結果匹配的字串。其中,模式pattern與 \"
、 \\
、 \n
匹配,同時也與除 "
之外任意一個字元匹配。
所以我們可以得到一個下圖的匹配規則regexPat。
ArrayList<Token> tokens
是詞法解析器 Lexer
中已解析的 Token
。
原始碼source作為引數傳入 Lexer
的 add
方法中。
在建立Token時,單元作為建構函式的引數。同時變數,數字值等單元我們無法通過 KindMap
類來判斷k值,所以需要在這裡判斷。
判斷是否是變數型別 |
---|
![]() |
判斷是否是數值型別 |
---|
![]() |
判斷是否是字串型別 |
---|
![]() |
三、效果
測試程式碼 |
---|
![]() |
Token解析 |
---|
![]() |
![]() |
將原始碼轉換為了相應的Token單元,這就是詞法解析的主要工作了。
接下來按照原始碼獲得轉換後的直譯程式碼。