Babylon-AST初探-程式碼生成(Create)

Summer肉欣發表於2018-05-31

  業餘時間寫了Babylon-AST的系列文章,這裡是第一篇,後面還有三篇。趁著今天有空都一起發上來啦。

  最近想研究react小程式程式碼的,後來感覺跨度有些大,因為平時也會寫一些vue的程式碼,而且vue小程式更接近一些,所以還是先做了一個vue小程式的PoC。可是這些都不是重點啊,重點是在這一過程中學習並使用了babylonAST。因為也是第一次接觸,所以想寫點筆記記錄一下,也希望能給大家一點參考。

  程式碼是寫出來的,一定一定多寫多練,所以我這裡還是以例項程式碼為主,涉及到的點也是在vue小程式中用到的,或者是轉換的基礎。可是也會有很多超出此範圍的知識點,我們這裡就先不做具體討論啦,這裡給出了一些參考資料,大家可以參考下。

涉及到的參考資料:

  1. AST explorer: https://astexplorer.net/ 神器,閱讀和書寫AST操作全靠它
  2. Babel Plugin Handbook https://github.com/jamiebuilds/babel-handbook/blob/master/translations/en/plugin-handbook.md 從這裡找到了一些API
  3. Babylon和babel-traverse詳解 https://github.com/xtx1130/blog/issues/7 其實也稱不上詳解,不過看到的比較早,算是篇啟蒙文章吧
  4. Babel types https://babeljs.io/docs/core-packages/babel-types/ 生成程式碼時會大量用到

AST explorer檢視程式碼

  首先開啟 https://astexplorer.net/ ,直接在程式碼裡輸入1,檢視基本效果。

Babylon-AST初探-程式碼生成(Create)

  我們要做的只是程式碼的轉換,這裡只需關心program.body部分即可(是否可以操作tokens來更改程式碼,還沒有研究)。

  如上圖,程式程式碼裡只有一個表示式ExpressionStatement。點選+展開,可以看到內部的細節,如下圖:

Babylon-AST初探-程式碼生成(Create)

  內部結構只有一個Literal,非常簡單。更復雜的程式碼,我們在後面再來解釋。

AST 的CRUD(Create-Retrieve-Update-Delete)

1. Create

  實際上,Create是個相對複雜的操作,通常會結合RetrieveUpdate使用。可以結合實際需要,選擇閱讀順序。

  首先,構造一個空的node工程,後續會基於該專案一步步擴充。

npm install babylon @babel/types @babel/generator @babel/traverse
複製程式碼

測試程式碼

const babylon = require('babylon')
const t = require('@babel/types')
const generate = require('@babel/generator').default
const traverse = require('@babel/traverse').default

const code = ''
const ast = babylon.parse(code)
// manipulate ast
const output = generate(ast, {}, code)  
console.log('Input \n', code)
console.log('Output \n', output.code)
複製程式碼

因為這裡是空的code,所以InputOutput都沒有輸出,只是搭一個程式碼結構。

構造一個1的程式碼

  根據上面的兩個圖,只有1程式碼由一個ExpressionStatement內嵌一個Literal組成。直接程式碼如下,可以參考下注釋。

const code = ''
const ast = babylon.parse(code)

// 生成literal
const literal = t.numericLiteral(1)
// 生成expressionStatement
const exp = t.expressionStatement(literal)  
// 將表示式放入body中
ast.program.body.push(exp)

const output = generate(ast, {}, code)
console.log('Input \n', code)
console.log('Output \n', output.code)
複製程式碼

執行輸出

Input

Output
 1;
複製程式碼

  這個例子中首先用到了t(babel-types)。可以在 https://babeljs.io/docs/core-packages/babel-types/ 檢視文件。t裡面有型別判斷和生成例項等方法。
  如t.expressionStatement(literal)就是生成一個ExpressionStatement,這個函式要用到的引數在文件中(https://babeljs.io/docs/core-packages/babel-types/#expressionstatement) 有描述,可是文件真心不好看,大家還是根據AST explorer中查詢對應的方法,然後在文件看個引數就好了:

Babylon-AST初探-程式碼生成(Create)

構造const a = 1的程式碼

  上述1的程式碼過於簡單,我們來生成const a = 1的程式碼。將const a = 1輸入到AST explorer中,檢視語法樹資訊如下圖:

Babylon-AST初探-程式碼生成(Create)

  在這段程式碼中涉及了VariableDeclaration, VariableDeclarator, Identifier, Literal幾個babel-typesLiteral中使用的是數字型別NumericLiteral。這時就可以分別檢視文件了,比如:VariableDeclaration

Babylon-AST初探-程式碼生成(Create)

  這個t.variableDeclaration有兩個引數kinddeclarations,第二個引數是個陣列。

  根據語法樹,一層層的生成程式碼如下:

const code = ''
const ast = babylon.parse(code)

// 生成 VariableDeclarator
const id = t.identifier('a')
const literal = t.numericLiteral(1)
const declarator = t.variableDeclarator(id, literal)

// 生成 VariableDeclaration
const declaration = t.variableDeclaration('const', [declarator])

// 將表示式放入body中
ast.program.body.push(declaration)

const output = generate(ast, {}, code)
console.log('Input \n', code)
console.log('Output \n', output.code)
複製程式碼

執行結果如下:

Input

Output
 const a = 1;
複製程式碼

Create總結

  根據AST explorer可以完美生成程式碼,常見的異常是引數沒有填對,特別是陣列什麼的,一定要注意。多結合API文件和從小的程式碼片段做起能夠規避這類錯誤。

  最後,生成一個稍複雜一點的程式碼。

function add(a, b) {
  return a + b
}
複製程式碼

AST樹如下

Babylon-AST初探-程式碼生成(Create)

  感興趣的同學可以先嚐試根據語法樹提示寫一寫,再看下面的對照程式碼,如果上面看懂了其實寫這個真心不是很難了。

const code = ''
const ast = babylon.parse(code)

// BinaryExpression a + b
const binaryExp = t.binaryExpression('+', t.identifier('a'), t.identifier('b'))
const returnStatement = t.returnStatement(binaryExp)

// function body
const fnBody = t.blockStatement([returnStatement])
const params = [t.identifier('a'), t.identifier('b')]

const fnDeclaraton = t.functionDeclaration(t.identifier('add'), params, fnBody)
ast.program.body.push(fnDeclaraton)

const output = generate(ast, {}, code)
console.log('Input \n', code)
console.log('Output \n', output.code)
複製程式碼

  以上,就是ASTCreate的介紹,想進一步學習的接著看後面幾篇文章哦

相關文章