1.概念html
抽象語法樹(abstract syntax code,AST)是源代碼的抽象語法結構的樹狀表示
。這裏特指編程語言的源代碼。前端
樹上的每一個節點都表示源代碼中的一種結構,之因此說是抽象的,是由於抽象語法樹並不會表示出真實語法出現的每個細節
,好比說,嵌套括號被隱含在樹的結構中,並無以節點的形式呈現。node
抽象語法樹
並不依賴於源語言的語法,也就是說語法分析
階段所採用的上下文無關文法,由於在寫文法時,常常會對文法進行等價的轉換(消除左遞歸,回溯,二義性等),這樣會給文法分析引入一些多餘的成分,對後續階段形成不利影響,甚至會使各個階段變得混亂。所以,不少編譯器常常要獨立地構造語法分析樹,爲前端,後端創建一個清晰的接口。git
抽象語法樹在不少領域有普遍的應用,好比瀏覽器,智能編輯器,編譯器等。github
2.爲什麼須要抽象語法樹(抽象語法樹做用)typescript
編程語言太多,須要一個統一的結構讓計算機識別。npm
做用:好比typescript
的類型檢查,IDE的語法高亮,代碼檢查,轉譯等等,都是須要先將代碼轉化成AST在進行後續的操做。編程
3.抽象語法樹的生成過程(編譯)segmentfault
js爲例:後端
詞法分析(lexical analysis)
:進行詞法分析的程序或者函數叫做詞法分析器(Lexical analyzer,簡稱Lexer),也叫掃描器(Scanner,例如typescript
源碼中的scanner.ts
),字符流
轉換成對應的Token流
。
tokenize:
tokenize就是按照必定的規則,例如token令牌(一般表明關鍵字,變量名,語法符號等),將代碼分割爲一個個的「串」,也就是語法單元)。涉及到詞法解析的時候,常會用到tokennize。
語法分析(parse analysis)
:是編譯過程的一個邏輯階段。語法分析的任務是在詞法分析的基礎上將單詞序列組合成語法樹,如「程序」,「語句」,「表達式」等等.語法分析程序判斷源程序在結構上是否正確。源程序的結構由上下文無關文法描述。
例如:對
const a = 1;
const b = a + 1;
複製代碼
的編譯過程。
圖片地址:www.processon.com/view/link/5…
詞法解析過程
:一邊掃描源代碼一邊進行分類,例如掃描到第一行const a = 1
,首先掃描到const
,會生成一個語法單元說這是關鍵字const
,接着掃描到a
,這是變量名a
,接着操做符=
,接着常量1
,等等,構成一個個token流。
語法分析過程
:將token流轉化爲一個有元素層級嵌套所組成的表明程序語法結構的樹,這個樹被叫作抽象語法樹AST。
4.擴展測試:如何將const a = 1
轉化成var a = 1
1. 新建一個testAst
的工程
mkdir testAst複製代碼
testAst
下新建test.js
文件
touch test.js複製代碼
testAst
下安裝esprima
的npm模塊,獲得ASTnpm i esprima --save複製代碼
test.js
寫入代碼
const esprima = require('esprima');
let code = 'const a = 1';
const ast = esprima.parseScript(code);
console.log(ast);
複製代碼
2.運行test.js
node test.js複製代碼
3.獲得生成的AST
也可經過esprima.org/demo/parse.…,輸入代碼,在線查看AST
testAst
下安裝estraverse
的npm模塊,遍歷更新ASTnpm i estraverse --save
複製代碼
4.修改代碼以下:
const esprima = require('esprima');
const estraverse = require('estraverse');
let code = 'const a = 1';
const ast = esprima.parseScript(code);
estraverse.traverse(ast, {
enter: function (node) {
node.kind = "var";
}
});
console.log(ast);複製代碼
5.運行test.js
,獲得更新事後的AST
testAst
下安裝escodegen
的npm模塊,獲得轉譯後的代碼npm i escodegen --save複製代碼
6.修改代碼以下:
const esprima = require('esprima');
const estraverse = require('estraverse');
const escodegen = require('escodegen');
let code = 'const a = 1';
const ast = esprima.parseScript(code);
estraverse.traverse(ast, {
enter: function (node) {
node.kind = "var";
}
});
const transformCode = escodegen.generate(ast);
console.log(transformCode);
複製代碼
7.運行test.js
,獲得轉譯後的代碼
參考文檔:
https://segmentfault.com/a/1190000012943992