AST in JS

переводчик JavaScript

            AST in JS

Статья впервые опубликована на:GitHub.com/US TB-Вуд, умри, о ты…

написать впереди

Ядром многих инструментов и библиотек, таких как Babel, Webpack, vue-cli и esLint, является проверка и анализ кода с помощью концепции абстрактного синтаксического дерева. AST используется в самых разных сценариях внешнего интерфейса.Например, в vue.js первым шагом в процессе преобразования шаблона, который мы написали в коде, в функцию рендеринга, является синтаксический анализ строки шаблона для генерации AST. Многие синтаксисы JS не подходят для понимания неподходящих программ, чтобы дать разработчикам лучший опыт программирования. Поэтому необходимо преобразовать исходный код в AST, чтобы он был более подходящим для анализа программы.Компилятор браузера обычно преобразует исходный код в AST для дальнейшего анализа и других операций. Понимая концепцию AST, очень полезно иметь глубокое понимание некоторых фреймворков и инструментов во внешнем интерфейсе.

Эта статья будет заключаться из следующих разделов:

  1. Сценарии использования AST
  2. Определение АСТ
  3. Парсер JavaScript (три оси)
  4. Преобразование стрелочных функций с помощью AST
  5. Плагин babel для предварительных вычислений с использованием AST

Сценарии использования AST

  • Проверка синтаксиса кода, проверка стиля кода, форматирование кода, подсветка кода, подсказки об ошибках кода, автодополнение кода и т. д.
    • Например, JSLint, JSHint для проверки ошибок кода или стиля, поиска возможных ошибок.
    • Подсказки IDE об ошибках, форматирование, выделение, автозаполнение и т. д.
  • обфускация кода
    • UglifyJS2 и т. д.
  • Оптимизировать и изменить код, изменить структуру кода, чтобы добиться желаемой структуры
    • Инструменты упаковки кода webpack, rollup и т. д.
    • Преобразование между спецификациями кода, такими как CommonJS, AMD, CMD, UMD и т. д.
    • Конвертируйте CoffeeScript, TypeScript, JSX и т. д. в собственный Javascript

Определение АСТ

  • Официальное определение АСТ:

В информатике абстрактное синтаксическое дерево (сокращенно абстрактное синтаксическое дерево или AST) или синтаксическое дерево (синтаксическое дерево) представляет собой древовидное представление абстрактной синтаксической структуры исходного кода, в частности исходного кода языка программирования. .

Вот онлайн-конвертер AST:преобразователь АСТ. Формат кода, преобразованного в AST, примерно такой, как показано на следующем рисунке:

Формат JSON после преобразования в AST примерно такой:

{
  "type": "Program",
  "start": 0,
  "end": 16,
  "body": [
    {
      "type": "FunctionDeclaration",
      "start": 0,
      "end": 16,
      "id": {
        "type": "Identifier",
        "start": 9,
        "end": 12,
        "name": "ast"
      },
      "expression": false,
      "generator": false,
      "params": [],
      "body": {
        "type": "BlockStatement",
        "start": 14,
        "end": 16,
        "body": []
      }
    }
  ],
  "sourceType": "module"
}

Поле типа в строковой форме указывает тип узла. Например, «BlockStatement», «Identifier», «BinaryExpression» и т. д. Каждый тип узла определяет некоторые свойства для описания типа узла. Затем вы можете анализировать другие операции через эти узлы.

Парсер JavaScript (три оси)

  • JavaScript Parser, парсер, который преобразует исходный код js в абстрактное синтаксическое дерево.

  • Браузер преобразует исходный код js в абстрактное синтаксическое дерево с помощью парсера, а затем преобразует его в байт-код или напрямую сгенерирует машинный код.

  • Вообще говоря, каждый движок js будет иметь свой собственный формат абстрактного синтаксического дерева, движок Chrome v8, движок Firefox SpiderMonkey и т. д. MDN предоставляет подробное описание формата SpiderMonkey AST, который является отраслевым стандартом.

Трехсторонний топор JS Parser

1. Преобразовать исходный код в AST через esprima
let esprima = require('esprima');
let code = 'function ast(){}';
let ast = esprima.parse(code);
console.log(ast);

После установки через npm i esprima -S запуск приведенного выше кода выведет:

Script {
  type: 'Program',
  body:
   [ FunctionDeclaration {
       type: 'FunctionDeclaration',
       id: [Identifier],
       params: [],
       body: [BlockStatement],
       generator: false,
       expression: false,
       async: false } ],
  sourceType: 'script' }
2. Пройдите и обновите AST через estraverse.
let esprima = require('esprima');
let estraverse = require('estraverse');
let code = 'function ast(){}';
let ast = esprima.parse(code);
estraverse.traverse(ast, {
  enter(node) {
    console.log('enter', node.type)
    if (node.type == 'Indentifier') {
      node.name += 'enter';
    }
  },
  leave(node) {
    console.log('leave', node.type)
    if (node.type == 'Indentifier') {
      node.name += 'leave';
    }
  }
})
console.log(ast);

После установки через npm i estraverse -S запуск приведенного выше кода выведет:

Script {
  type: 'Program',
  body:
   [ FunctionDeclaration {
       type: 'FunctionDeclaration',
       id: [Identifier],
       params: [],
       body: [BlockStatement],
       generator: false,
       expression: false,
       async: false } ],
  sourceType: 'script' }
3. Восстановите AST в исходный код через escodegen.
t esprima = require('esprima');
let estraverse = require('estraverse');
let escodegen = require('escodegen');
let code = 'function ast(){}';
let ast = esprima.parse(code);
estraverse.traverse(ast, {
  enter(node) {
    console.log('enter', node.type)
    if (node.type == 'Identifier') {
      node.name += '_enter';
    }
  },
  leave(node) {
    console.log('leave', node.type)
    if (node.type == 'Identifier') {
      node.name += '_leave';
    }
  }
});
let result = escodegen.generate(ast)
console.log(result);

После установки через npm i escodegen -S выполнение приведенного выше кода выведет:

  function ast_enter_leave() {
}

Таким образом, положить

  function ast() {
}

Изменено на:

  function ast_enter_leave() {
}

конверсионная стрелочная функция

Используйте babel-core (основная библиотека babel, реализация основного механизма преобразования) и babel-types (может реализовать суждение о типах, генерировать узлы AST и т. д.) и AST для преобразования

let sum = (a, b) => a + b

Измените его на:

let sum = function(a, b) {
  return a + b
}

Код реализации выглядит следующим образом:

// babel核心库,实现核心的转换引擎
let babel = require('babel-core');
// 可以实现类型判断,生成AST节点等
let types = require('babel-types');

let code = `let sum = (a, b) => a + b`;
// let sum = function(a, b) {
//   return a + b
// }

// 这个访问者可以对特定类型的节点进行处理
let visitor = {
  ArrowFunctionExpression(path) {
    console.log(path.type);
    let node = path.node;
    let expression = node.body;
    let params = node.params;
    let returnStatement = types.returnStatement(expression);
    let block = types.blockStatement([
        returnStatement
    ]);
    let func = types.functionExpression(null,params, block,false, false);
    path.replaceWith(func);
  }
}

let arrayPlugin = { visitor }
// babel内部会把代码先转成AST, 然后进行遍历
let result = babel.transform(code, {
  plugins: [
    arrayPlugin
  ]
})
console.log(result.code);

Плагин babel для предварительных вычислений с использованием AST

Код реализации выглядит следующим образом:

// 预计算简单表达式的插件
let code = `const result = 1000 * 60 * 60`;
let babel = require('babel-core');
let types= require('babel-types');

let visitor = {
  BinaryExpression(path) {
    let node = path.node;
    if (!isNaN(node.left.value) && ! isNaN(node.right.value)) {
      let result = eval(node.left.value + node.operator + node.right.value);
      result = types.numericLiteral(result);
      path.replaceWith(result);
      let parentPath = path.parentPath;
      // 如果此表达式的parent也是一个表达式的话,需要递归计算
      if (path.parentPath.node.type == 'BinaryExpression') {
        visitor.BinaryExpression.call(null, path.parentPath)
      }
    }
  }
}

let cal = babel.transform(code, {
  plugins: [
    {visitor}
  ]
});

Справочная статья

Абстрактное синтаксическое дерево AST

Абстрактное синтаксическое дерево JS, которое вы можете понять с первого взгляда

Углубленный Вавилон, этого достаточно

Курс обучения фронтенд-архитектора Everest

Можете обратить внимание на мой паблик-аккаунт «Muchen Classmate», фермера на гусиной фабрике, который обычно записывает какие-то банальные мелочи, технологии, жизнь, инсайты и срастается.