亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

深入編譯器——第一部分:詞法解析和Scanner(介紹ECMAScript的詞法規(guī)范和TypeScr

pingan8787 / 1721人閱讀

摘要:詞法分析對構(gòu)成源程序的字符流進行掃描然后根據(jù)構(gòu)詞規(guī)則識別單詞也稱單詞符號或符號。語義分析是編譯過程的一個邏輯階段語義分析的任務(wù)是對結(jié)構(gòu)上正確的源程序進行上下文有關(guān)性質(zhì)的審查進行類型審查,審查抽象語法樹是否符合該編程語言的規(guī)則。

1. 文章的內(nèi)容和主題
我對編譯器的深入了解起源于一條推特中的問題:Angular是如何用Angular預(yù)先編譯器(AOT)對靜態(tài)代碼進行解析工作的。在進行一些debugging后,我發(fā)現(xiàn)AOT非常依賴TypeScript編譯器,所以我開始對它進行反編譯(reverse-engineer)。有趣的是,大部分編譯器都使用一樣的規(guī)則,這些規(guī)則被廣泛的認為是編譯器理論。在理解編譯器的內(nèi)部機制時,對這些理論一窺究竟是非常有必要的。
接下來我將描述對每個編譯器的第一階段都非常重要的詞法分析
這篇文章盡量少的參入理論和教條主義,不過大部分依然是理論性的。在最后一章,我將展示TypeScript scanner是如何工作的并提供相關(guān)的鏈接。

TypeScript 語法是基于ECMAScript 規(guī)范的,我希望讀者們能夠保持足夠的好奇心查看文章中的鏈接,并且熟練掌握這些規(guī)范。 如果你能做到這些,你就會知道這些語法,并且在JavaScript的新特新被寫入MDN之前就學習到了。如果你讀完了這篇文章,可以通過理解裝飾器(decorator)規(guī)范里描述的裝飾器的語法特性來測試自己。
這篇文章比較長,因此你不需要一次性全部讀完。一點一點的讀這篇文章,有足夠的時間記住文章里的內(nèi)容。如果你一直想知道ECMAScript 規(guī)范或者想弄清楚編譯器是如何工作的,那就開始讀這篇文章吧!

2.編譯器編譯過程中的幾個階段

編譯器就是把一個用一種編程語言寫成的程序編譯成另一種語言的電腦程序。編譯器首先需要理解原來的輸入的編程語言 ,然后把它編譯成目標語言。由于這兩種不同的特性,需要把編譯器的功能分成兩大塊:前端(a front-end)和后端(a back-end.)。前段處理輸入源程序,后端處理輸出目標代碼。

編譯器可以看成是一個由多個階段構(gòu)成的流水線結(jié)構(gòu),上一步的結(jié)果輸入到下一步,然后下一步再優(yōu)化代碼并且轉(zhuǎn)化成這一步的需要的代碼,最后又傳給下一步。前端包括三個主要的階段就是詞法分析,語法分析和語義分析。

詞法分析對構(gòu)成源程序的字符流進行掃描然后根據(jù)構(gòu)詞規(guī)則識別單詞(也稱單詞符號或符號)。

語法分析是編譯過程的一個邏輯階段。語法分析的任務(wù)是在詞法分析的基礎(chǔ)上將單詞序列組合成各類語法短語,并生成抽象語法書(AST).語法分析程序判斷源程序在結(jié)構(gòu)上是否正確。

語義分析是編譯過程的一個邏輯階段. 語義分析的任務(wù)是對結(jié)構(gòu)上正確的源程序進行上下文有關(guān)性質(zhì)的審查, 進行類型審查,審查抽象語法樹是否符合該編程語言的規(guī)則。

這篇文章主要目的在于介紹詞法分析。

3. 形式語言的語法
在我們開始談詞法分析之前,我們需要聊一點自然語言和形式語言(Formal language
是用精確的數(shù)學或機器可處理的公式定義的語言)和他們的語法。像英語和法語這樣的自然語言通常用于日常交流,而且自然發(fā)展而來的。形式語言,一方面。是由人類設(shè)計用來特殊的用途的——比如編程語言用來表示計算機的語言,數(shù)學符號表示數(shù)字之間的關(guān)系等等。
無論是自然語言還是形式語言都可以用語法來描述。語法指該語言中的句子、短語、詞匯的邏輯、結(jié)構(gòu)特征以及構(gòu)成方式,而語法包括對語法規(guī)律進行的總結(jié)描述或?qū)φZ言使用的規(guī)范或限定。自然語言的語法是非常復雜的,并通過經(jīng)驗主義的方式來研究的。另一方面,形式語言通常都是簡單的,并根據(jù)我們的需求定義的。取決于我們可以通過怎樣的方式分辨幾種語法來定義規(guī)則。

詞法描述了一種語言的詞匯結(jié)構(gòu),就是語言中每個單詞(符號)。比如,d都是JavaScript 的字母,但是語法并沒有定義在正常語句中后面跟d的規(guī)則,所以當你執(zhí)行d的代碼的時候,我們會得到無效符號的語法錯誤:

d
Uncaught SyntaxError: Invalid or unexpected token

語法定義了語句的結(jié)構(gòu),就是單詞符號在一條語句中組合方式。例如,JavaScript詞法定義的 varconst,在語法中沒有var后面跟著const,所有當下面這樣使用時就會出現(xiàn)語法錯誤:

var const
Uncaught SyntaxError: Unexpected token const

上面的結(jié)構(gòu)根據(jù)ECMAScript語法規(guī)范是無效的,所以編譯器并不會識別var后面跟著const這樣的語句。

3. 詞法分析
詞法分析是編譯器在處理源代碼時三個階段中的第一個階段。詞法分析的作用就是把源代碼分解成被稱為是標記(token)的子字符串,并且對每個標記進行分類,進行詞法分析的程序或者函數(shù)叫作詞法分析器(lexical
analyzer,簡稱lexer),也叫掃描器(scanner)。它們讀取輸入字符流,按照詞法生成標記,這個過程叫做標記化(tokenization)。如果一組字符串沒有匹配的規(guī)則掃描器就會報錯。這就是我們例子中d出現(xiàn)報錯的原因。
掃描器對每一個被識別的標記都會按語法分配一個語句范疇(syntactic category)。這個范疇或者說ECMAScript的標記種類非常廣泛,包括但不限于識別碼(Identifier),數(shù)字文字(NumericLiteral),字符串文字(StringLiteral )和各種不同的像const、let、if這樣的關(guān)鍵字。

所以詞法分析階段的輸出通常是由帶有對應(yīng)類型的標記和帶有詞位的子字符串組成的隊列:

{class: SyntaxKind.ConstKeyword, lexeme: ‘const’}
如果你對ECMAScript 定義的標記類型的感興趣,可以查看SyntaxKind的列舉。

詞法分析器可以掃描整個源代碼然后輸出完整的標記隊列,或者緩慢的掃描一次輸出一個標記。掃描器把在解析前將整個源代碼轉(zhuǎn)化成標記序列而消耗不必要的內(nèi)存是不常見的。所以掃描器只有在代碼需要被解析時才工作,TypeScript 掃描器也一樣。TS掃描器在另一方面也非常有趣。JavaScript 語法只定義了一些語言結(jié)構(gòu),如常用表達和模板文字,這將導致解析的歧義,所以需要掃描器根據(jù)解析上下文來識別不同的字符集。
由于解析上下文是由解析器定義的,當請求一個標記時,TS掃描器可以被稱為解析驅(qū)動。我會在多個目標符號部分詳解這個復雜的問題。

4.定義標記

我們用JavaScript在定義一個變量這個例子來演示語法規(guī)則是如何工作的。在JavaScript中,我們可以像下面這樣用const來定義一個變量:

const v = 3

我們簡單的假設(shè)初始值是一個數(shù)字。當你看這段代碼時,可以清楚的看到const定義了一個變量v,用=給這個變量分配了一個數(shù)字3的初始值。
顯然。掃描器并不是這樣工作的。由于ECMAScript 用Unicode 符號定義了程序碼,所以編譯中的這段代碼看起來是這樣的:

c   o    n    s    t        v        =       3
99, 111, 110, 115, 116, 32, 118, 32, 61, 32, 51

Now its job is to split the expression into tokens and categorize them so the following list of tokens is produced:

現(xiàn)在編譯器的工作就是對這段表達式分割成標記,并且對它們進行分類,然后就生成了下面的這組符號:

{class: SyntaxKind.ConstKeyword, lexeme: "const"}
{class: SyntaxKind.Identifier, lexeme: "v"}
{class: SyntaxKind.EqualsToken, lexeme: "="}
{class: SyntaxKind.NumericLiteral, lexeme: "3"}

如果用let替代const第一個標記應(yīng)為SyntaxKind.LetKeyword。

5.常規(guī)語法

ECMAScript 就是解析用Unicode 的符號作為標記的規(guī)則的正常語法。根據(jù)Chomsky對語法的分類,常規(guī)語法是最受約束的并且最缺乏表達能力的語法。它僅適合于描述標記是如何被組合的,但不能描述句子的結(jié)構(gòu)。然而,一個語法規(guī)則越不自由越容易描述和解析。因為我們?nèi)绱岁P(guān)心定義和解析標記,所以這是一個理想的語法。
這個系列的下一篇文章我們將會了解上下文無關(guān)文法(context-free grammar)。這類語法允許遞歸的結(jié)構(gòu),并且用來定義程序的結(jié)構(gòu)。
值得注意的是,很多教育資料在解釋掃描器并不用常規(guī)語法,而是用常規(guī)表達定義定義常規(guī)規(guī)范。但是,由于ECMAScript 用了常規(guī)語法,我會在這篇文章中解釋它。

6.了解這個語法

Now, let’s try to see how we can construct the grammar and the rules that help TypeScript identify the list of tokens I showed above. Here it is again and we need to define rules for recognizing each token in the statement:
現(xiàn)在,讓我們嘗試看看我們怎樣構(gòu)建語法和規(guī)則來幫助TypeScript我在上面列出的標記。下面又是我們需要在表達式中識別的每一個符號:

const v = 3
{class: SyntaxKind.ConstKeyword, lexeme: "const"}
{class: SyntaxKind.Identifier, lexeme: "v"}
{class: SyntaxKind.EqualsToken, lexeme: "="}
{class: SyntaxKind.NumericLiteral, lexeme: "3"}

語法中的每一項規(guī)則是用生產(chǎn)方式來定義的。生產(chǎn)方式是可以遞歸生成新的符號序列的替代規(guī)則。在JavaScript 中我們可以用const或者let來聲明一個變量,于是我們可以用關(guān)鍵字符號定義下面的規(guī)則:

Keyword ::
    const
    let

這個關(guān)鍵字符號的規(guī)則有兩個結(jié)果,這兩個結(jié)果表示符號關(guān)鍵字可以是let或者const字符串。合成變量的關(guān)鍵字被稱作非終結(jié)的,意味著他有結(jié)果并且可以被替代。這個替代性通常被認為能被分解成更小的單位。const和let所產(chǎn)生的結(jié)果被稱為終結(jié)符,不能被分解成更小的單位。沒有結(jié)果的終端符號在源碼中找到。非終結(jié)符是可以被取代的符號。一個形式文法中必須有一個起始符號;這個起始符號屬于非終結(jié)符的集合。ECMAScript定義了許多其他的非終結(jié)符關(guān)鍵字例如:if, else, for, do, while, function, class等等。

可以用下面的任意布局來定義ECMAScript語法:
non_terminal_symbol ::
  symbol1 symbol2  (production rule 1, Symbol1 followed by Symbol2)
  symbol3 symbol4  (production rule 2, Symbol3 followed by Symbol4)

::左邊的稱作左邊部分,右邊的稱為右邊部分。對于常規(guī)的和上下文無關(guān)語法非終結(jié)符只能在左邊,右邊可以是終結(jié)符也可以是非終結(jié)符
然而對于常規(guī)語法,只能是下面的一種:

只有終結(jié)字符的

或者有終結(jié)字符和單個非終結(jié)字符,并且終結(jié)字符在開始或者結(jié)尾。

non_terminal_symbol ::
  terminal_symbol
non_terminal_symbol ::
  terminal_symbol non_terminal_symbol   (right-linear)
non_terminal_symbol ::
  non_terminal_symbol terminal_symbol   (left-linear)

上下文無關(guān)語法更加寬松,允許任意數(shù)量的終結(jié)字符和非終結(jié)字符在右邊。常規(guī)語法和上下文無關(guān)語法都可以有任意數(shù)量的符號在左邊:

non_terminal_symbol ::
  production rule 1
  production rule 2
  ...
  production rule n

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/100628.html

相關(guān)文章

  • 如何編寫簡單parser(基礎(chǔ)篇)

    摘要:在這里,詞法解析器應(yīng)用的規(guī)則即為詞匯語法的定義,語法解釋器應(yīng)用的規(guī)則即為表達式語句聲明和函數(shù)等的定義。如何編寫簡單的實踐篇 什么是parser? 簡單的說,parser的工作即是將代碼片段轉(zhuǎn)換成計算機可讀的數(shù)據(jù)結(jié)構(gòu)的過程。這個計算機可讀的數(shù)據(jù)結(jié)構(gòu)更專業(yè)的說法是抽象語法樹(abstract syntax tree),簡稱AST。AST是代碼片段具體語義的抽象表達,它不包含該段代碼的所有細...

    Barry_Ng 評論0 收藏0
  • 由 ECMA 規(guī)范解讀 Javascript 可執(zhí)行上下文概念

    摘要:不包括作為其嵌套函數(shù)的被解析的源代碼。作用域鏈當代碼在一個環(huán)境中執(zhí)行時,會創(chuàng)建變量對象的一個作用域鏈。棧結(jié)構(gòu)最頂層的執(zhí)行環(huán)境稱為當前運行的執(zhí)行環(huán)境,最底層是全局執(zhí)行環(huán)境。無限制函數(shù)上下文?;蛘邟伋霎惓M顺鲆粋€執(zhí)行環(huán)境。 前言 其實規(guī)范這東西不是給人看的,它更多的是給語言實現(xiàn)者提供參考。但是當碰到問題找不到答案時,規(guī)范往往能提供想要的答案 。偶爾讀一下能夠帶來很大的啟發(fā)和思考,如果只讀一...

    daryl 評論0 收藏0
  • 理解JavaScript核心知識點:This

    摘要:關(guān)鍵字計算為當前執(zhí)行上下文的屬性的值。毫無疑問它將指向了這個前置的對象。構(gòu)造函數(shù)也是同理。嚴格模式無論調(diào)用位置,只取顯式給定的上下文綁定的,通過方法傳入的第一參數(shù),否則是。其實并不屬于特殊規(guī)則,是由于各種事件監(jiān)聽定義方式本身造成的。 this 是 JavaScript 中非常重要且使用最廣的一個關(guān)鍵字,它的值指向了一個對象的引用。這個引用的結(jié)果非常容易引起開發(fā)者的誤判,所以必須對這個關(guān)...

    TerryCai 評論0 收藏0
  • 參數(shù)默認值引起第三作用域

    摘要:如果形參有設(shè)置默認值,第二個就被建立,他針對的是函數(shù)體內(nèi)的聲明我們可以形象的理解為這是一個除了函數(shù)作用域和塊級作用域之外的第三作用域。 開門見山,我們來看看下面這個有趣的例子 showImg(http://ogitl0zvo.bkt.clouddn.com/public/16-11-12/77445738.jpg); ?對于上面這種用var的聲明方式,無論x的默認值為什么,只要形參中出...

    Fourierr 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<