天天看點

Kotlin你不知道的秘密(一)

Kotlin你不知道的秘密(一)

本篇主要說一下Koltin分支中的秘密的第一個:Koltin關鍵字(final/if/for)、運算符(+/-/?:)是如何被識别的?陸續後面還有會3-4篇來介紹其他秘密,文章中提到的代碼和其他資料已開源到Android知識體系& Android-Body

人與人之間主要通過語言來進行溝通,那程式之間可以溝通嗎?是靠什麼進行溝通的?

答案是肯定的,人與人之間的溝通主要是靠語言,程式之間也是可以靠語言溝通的。語言大緻分為中文、英文、日文等其目的是統一不同風格的文化進行交流。程式又有C、Python、JAVA、Kotlin等其目的也是不同風格的程式語言與人交流,但是最後都會轉化成計算機指令供機器識别、執行。人的語言是多樣的又是多變的,之是以多樣和多變主要是人和人是不同的,每個人都有自己的思維方式和特點。了解一句話可能有不同的意思但是程式是固定的又是嚴謹的容不得一絲的錯誤和漏洞,這也就是程式容易出Bug的原因。比如:

小美:今晚我們約一下
  • 人:約我幹嘛?我是不是要穿個西服?做個發型?
  • 程式:滾

程式就是這麼高冷,因為他是嚴謹的你必須告訴他約會的時間、約會的地點、約會要幹什麼、幾個人約會、需要帶什麼東西、要約會多久、需要帶錢嗎、今晚還用回家嗎等等。我們知道JAVA、Kotlin都是可以運作在jvm之上的,又同時會把語言轉化成位元組碼供機器識别,他們又有各自的特定和風格但是大緻是符合語言組成的定義的:詞法、文法、語義。Kotlin現在被谷歌主推的有許多官方庫和gradle庫也都改造成了Koltin的可見其重要性。今天我們就分析下kotlin的主要優勢有哪些是如何轉化成機器碼的。相信大家常聽到的話就是知其表不知其裡。所有我們分析下Koltin的其裡。

Koltin關鍵字(final/if/for)、運算符(+/-/?:)是如何被識别的?

其實這個問題可以了解為輸入的每個字元如何被識别成單詞的?簡稱:詞法分析

詞法分析:詞法分析階段是編譯過程的第一個階段,是編譯的基礎。這個階段的任務是從左到右一個字元一個字元地讀入源程式,即對構成源程式的字元流進行掃描然後根據構詞規則識别單詞(也稱單詞符号或符号) – 百科
Kotlin你不知道的秘密(一)

可以看到在KtTokens中有我們常見的關鍵字和運算符還有辨別符、通路權限修飾符等等。這裡主要扮演的角色是Token流。其實這個就是将所有的Kotlin詞法單元一一枚舉出來并分組以後,再進行詞法分析。可以看到他們并不是單純的定義字元串,而是通過不同類型的KtToken來建立,但是他們都是繼承與IElementType。

public class KtToken extends IElementType {
    public KtToken(@NotNull @NonNls String debugName) {
        super(debugName, KotlinLanguage.INSTANCE);
    }
}
複制代碼
           

IElementType是文法樹(AST)節點類型,文法樹是什麼? 後面再介紹。比較有意思的是可以看到super中接收了兩個參數,第一個debugName也就是我們定義的關鍵字運算符還有辨別符之類的,第二個參數則是傳入的KotlinLanguage類

public class KotlinLanguage extends Language {
    @NotNull
    public static final KotlinLanguage INSTANCE = new KotlinLanguage();
    public static final String NAME = "Kotlin";

    private KotlinLanguage() {
        super("kotlin");
    }
}

public class KotlinFileType extends LanguageFileType {
    public static final String EXTENSION = "kt";
    public static final KotlinFileType INSTANCE = new KotlinFileType();

    private final NotNullLazyValue<Icon> myIcon = new NotNullLazyValue<Icon>() {
        @NotNull
        @Override
        protected Icon compute() {
            return KotlinIconProviderService.getInstance().getFileIcon();
        }
    };

    @Override
    @NotNull
    public String getName() {
        return KotlinLanguage.NAME;
    }

    @Override
    public Icon getIcon() {
        return myIcon.getValue();
    }
}
複制代碼
           

KotlinLanguage extends Language顧名思義是聲明Koltin語言,KotlinFileType是定義Koltin檔案以 .kt 結尾還有koltin的Icon之類的。在psi.idea目錄下。腦洞可以大開一下,我們是不是可以定義自己的語言并且以自己想結尾的檔案命名比如.wm ,答案是肯定的。感興趣的可以實踐下。

詞法分析器

我們看到在KtTokens中有許多我們熟悉的關鍵字和運算符比如:null、false、&&、?:。都是我們常用的,在我們輸入後他是由多個字元組成的,是以多個字元的組合是不是我們的關鍵字還是一組無意義的字元那,這時候就用到了詞法分析器。

詞法分析器:詞法分析是将字元序列轉換為單詞(Token)序列的過程。進行詞法分析的程式或者函數叫作詞法分析器(簡稱Lexer),也叫掃描器。 – 百科
Kotlin你不知道的秘密(一)

可以看到Koltin使用的開源的詞法分析器:JFlex(https://github.com/jflex-de/jflex/)。首先定義一個可以添加詞法規則以**“.flex” 結尾的檔案。

1 表示使用者代碼段:這個段中的所有内容将被拷貝到生成的詞法類的類聲明之前。在這個段中,常見的是 package 和 import 語句。如圖我們添加了import java.util.*;、import org.jetbrains.kotlin.lexer.KtTokens;等

2 表示參數設定和聲明段:用來定制詞法分析器,包括類名、父類、權限修飾符等等,以%開頭作為标記。如:%class _JetLexer

3 表示詞法規則:一組正規表達式和動作,也就是當正規表達式比對成功後要執行的代碼

定義完後JFlex會讀取配置檔案并生成一個詞法分析器_JetLexer類,如果存在兩個正則表達 式"no"和note",掃描器會比對"note",因為它是最長的。如果兩個正規表達式完全 相同,具有相同的長度,那麼掃描器将比對最先列在說明中的表達式。如果沒有比對的正規式,詞法分析器将終止對輸入流的分析并給出錯誤消息。

public class KotlinLexer extends FlexAdapter {
    public KotlinLexer() {
        super(new _JetLexer((Reader) null));
    }
}

public class FlexAdapter extends LexerBase {

    public void start(@NotNull CharSequence buffer, int startOffset, 
                        int endOffset, int initialState) {
        if (buffer == null) {
            $$$reportNull$$$0(1);
        }

        this.myText = buffer;
        this.myTokenStart = this.myTokenEnd = startOffset;
        this.myBufferEnd = endOffset;
        this.myFlex.reset(this.myText, startOffset, endOffset, initialState);
        this.myTokenType = null;
    }
複制代碼
           

生成的_JetLexer最終被KotlinLexer引用。而KotlinLexer繼承于LexerBase,Lexer中有個重要的方法就是start(buffer,startOffset,endOffset),他們分别代表的意思也就是輸入的字元、字元開始的偏移量,字元的結束的偏移量。_JetLexer裡面是處理各種字元的地方,主要實作的方法是 advance(),會根據輸入的字元比對到自己定義的一些關鍵字運算符,然後再輸出。

總結

知其表也要知其裡 綜合上面的知識我們就可以得出Koltin關鍵字(final/if/for)、運算符(+/-/?:)是如何被識别的。大概分為4步輸入源、掃描、分析、輸出如下:

1、我們在Studio中輸入if、final關鍵字等(簡稱:輸入源)

2、Lexer中的start方法會拿到我們輸入的字元串(簡稱:掃描)

3、_JetLexer的advance方法會根據輸入比對正則(簡稱:分析)

4、比對到對應正則後會輸出在KtTokens中定義的關鍵字和運算符等(簡稱:輸出)