成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

pingan8787 / 1653人閱讀

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

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

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

2.編譯器編譯過程中的幾個(gè)階段

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

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

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

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

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

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

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

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

d
Uncaught SyntaxError: Invalid or unexpected token

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

var const
Uncaught SyntaxError: Unexpected token const

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

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

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

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

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

4.定義標(biāo)記

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

const v = 3

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

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)在編譯器的工作就是對(duì)這段表達(dá)式分割成標(biāo)記,并且對(duì)它們進(jìn)行分類,然后就生成了下面的這組符號(hào):

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

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

5.常規(guī)語法

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

6.了解這個(gè)語法

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)在,讓我們嘗試看看我們?cè)鯓訕?gòu)建語法和規(guī)則來幫助TypeScript我在上面列出的標(biāo)記。下面又是我們需要在表達(dá)式中識(shí)別的每一個(gè)符號(hào):

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

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

Keyword ::
    const
    let

這個(gè)關(guān)鍵字符號(hào)的規(guī)則有兩個(gè)結(jié)果,這兩個(gè)結(jié)果表示符號(hào)關(guān)鍵字可以是let或者const字符串。合成變量的關(guān)鍵字被稱作非終結(jié)的,意味著他有結(jié)果并且可以被替代。這個(gè)替代性通常被認(rèn)為能被分解成更小的單位。const和let所產(chǎn)生的結(jié)果被稱為終結(jié)符,不能被分解成更小的單位。沒有結(jié)果的終端符號(hào)在源碼中找到。非終結(jié)符是可以被取代的符號(hào)。一個(gè)形式文法中必須有一個(gè)起始符號(hào);這個(gè)起始符號(hào)屬于非終結(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)

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

只有終結(jié)字符的

或者有終結(jié)字符和單個(gè)非終結(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ù)量的符號(hào)在左邊:

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/100628.html

相關(guān)文章

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

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

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

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

    daryl 評(píng)論0 收藏0
  • 理解JavaScript核心知識(shí)點(diǎn):This

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

    TerryCai 評(píng)論0 收藏0
  • 參數(shù)默認(rèn)值引起第三作用域

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

    Fourierr 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<