Расширенные основы интерфейса — абстрактное синтаксическое дерево JavaScript AST

JavaScript

предисловие

В настоящее время Babel является самым популярным компилятором кода JavaScript, который использует парсер JavaScript.babel-parser, первоначально изAcornпроектforkот. Acorn очень быстрый, простой в использовании и имеет архитектуру на основе плагинов, предназначенную для нестандартных функций (и тех, которые станут стандартными в будущем). Эта статья в основном знакомитesprimaРазобрать сгенерированный узел абстрактного синтаксического дерева,esprimaРеализация также основана на Acorn.

исходный адрес

Парсер Парсер

JavaScript Parser — это парсер, который преобразует исходный код js в абстрактное синтаксическое дерево (AST). Этот этап делится на два этапа:Лексический анализиСинтаксический анализ.

Общий анализатор JavaScript:

лексический анализ

На этапе лексического анализа код в виде строки преобразуется в поток токенов. Вы можете думать о токене как о плоском массиве фрагментов синтаксиса.

n * n;

Например вышеn*nРезультаты лексического анализа следующие:

[
  { type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
  { type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
  { type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
]

Каждый тип имеет набор свойств, описывающих токен:

{
  type: {
    label: 'name',
    keyword: undefined,
    beforeExpr: false,
    startsExpr: true,
    rightAssociative: false,
    isLoop: false,
    isAssign: false,
    prefix: false,
    postfix: false,
    binop: null,
    updateContext: null
  },
  ...
}

Как и узлы AST, они также имеют свойства start, end и loc.

Разбор

Синтаксический анализ заключается в преобразовании результата лексического анализа, то есть токенов, в AST.

function square(n) {
  return n * n;
}

Как показано в приведенном выше коде, сгенерированная структура AST выглядит следующим образом:

{
  type: "FunctionDeclaration",
  id: {
    type: "Identifier",
    name: "square"
  },
  params: [{
    type: "Identifier",
    name: "n"
  }],
  body: {
    type: "BlockStatement",
    body: [{
      type: "ReturnStatement",
      argument: {
        type: "BinaryExpression",
        operator: "*",
        left: {
          type: "Identifier",
          name: "n"
        },
        right: {
          type: "Identifier",
          name: "n"
        }
      }
    }]
  }
}

В следующих разделах объясняется каждый тип узла AST. Более поколение AST, запись выглядит следующим образом:

комбинироватьинструмент визуализации,Например

Следующий код:

var a = 42;
var b = 5;
function addA(d) {
    return a + d;
}
var c = addA(2) + b;

После первого лексического анализа он растет, как показано на следующем рисунке:

Синтаксический анализ, создание абстрактного синтаксического дерева, сгенерированное абстрактное синтаксическое дерево показано на следующем рисунке.

Base

Node

Все типы узлов реализуют следующие интерфейсы:

interface Node {
  type: string;
  range?: [number, number];
  loc?: SourceLocation;
}

Поле типа представляет собой строку, представляющую тип варианта AST. Поле loc представляет исходную информацию о местоположении узла. Если синтаксический анализатор не выдает информации об исходной позиции узла, это поле пустое; в противном случае это объект, который включает в себя начальную позицию (положение первого символа анализируемой исходной области) и конечную позицию.

interface SourceLocation {
    start: Position;
    end: Position;
    source?: string | null;
}

Каждый объект Position состоит из номера строки (с индексом 1) и номера столбца (с индексом 0):

interface Position {
    line: uint32 >= 1;
    column: uint32 >= 0;
}

Programs

interface Program <: Node {
    type: "Program";
    sourceType: 'script' | 'module';
    body: StatementListItem[] | ModuleItem[];
}

Представляет полное исходное дерево.

Scripts and Modules

Есть два источника номеров исходного кода, один из них — сценарий сценария, а другой — модуль модулей.

Когда это сценарий, телоStatementListItem. Когда это модули, телоModuleItem.

типStatementListItemиModuleItemТипы следующие.

type StatementListItem = Declaration | Statement;
type ModuleItem = ImportDeclaration | ExportDeclaration | StatementListItem;

ImportDeclaration

синтаксис импорта, модуль импорта

type ImportDeclaration {
    type: 'ImportDeclaration';
    specifiers: ImportSpecifier[];
    source: Literal;
}

ImportSpecifierТипы следующие:

interface ImportSpecifier {
    type: 'ImportSpecifier' | 'ImportDefaultSpecifier' | 'ImportNamespaceSpecifier';
    local: Identifier;
    imported?: Identifier;
}

ImportSpecifierСинтаксис следующий:

import { foo } from './foo';

ImportDefaultSpecifierСинтаксис следующий:

import foo from './foo';

ImportNamespaceSpecifierСинтаксис выглядит следующим образом

import * as foo from './foo';

ExportDeclaration

Тип экспорта следующий

type ExportDeclaration = ExportAllDeclaration | ExportDefaultDeclaration | ExportNamedDeclaration;

ExportAllDeclarationэкспорт из указанного модуля

interface ExportAllDeclaration {
    type: 'ExportAllDeclaration';
    source: Literal;
}

Синтаксис следующий:

export * from './foo';

ExportDefaultDeclarationэкспортировать модуль по умолчанию

interface ExportDefaultDeclaration {
    type: 'ExportDefaultDeclaration';
    declaration: Identifier | BindingPattern | ClassDeclaration | Expression | FunctionDeclaration;
}

Синтаксис следующий:

export default 'foo';

ExportNamedDeclarationэкспортировать некоторые модули

interface ExportNamedDeclaration {
    type: 'ExportNamedDeclaration';
    declaration: ClassDeclaration | FunctionDeclaration | VariableDeclaration;
    specifiers: ExportSpecifier[];
    source: Literal;
}

Синтаксис следующий:

export const foo = 'foo';

Declarations and Statements

declaration, то есть объявление типа:

type Declaration = VariableDeclaration | FunctionDeclaration | ClassDeclaration;

statements, который является оператором типа:

type Statement = BlockStatement | BreakStatement | ContinueStatement |
    DebuggerStatement | DoWhileStatement | EmptyStatement |
    ExpressionStatement | ForStatement | ForInStatement |
    ForOfStatement | FunctionDeclaration | IfStatement |
    LabeledStatement | ReturnStatement | SwitchStatement |
    ThrowStatement | TryStatement | VariableDeclaration |
    WhileStatement | WithStatement;

VariableDeclarator

Для объявлений переменных атрибут kind указывает тип объявления, потому что ES6 представил const/let.

interface VariableDeclaration <: Declaration {
    type: "VariableDeclaration";
    declarations: [ VariableDeclarator ];
    kind: "var" | "let" | "const";
}

FunctionDeclaration

объявление функции (выражение, не являющееся функцией)

interface FunctionDeclaration {
    type: 'FunctionDeclaration';
    id: Identifier | null;
    params: FunctionParameter[];
    body: BlockStatement;
    generator: boolean;
    async: boolean;
    expression: false;
}

Например:

function foo() {}

function *bar() { yield "44"; }

async function noop() { await new Promise(function(resolve, reject) { resolve('55'); }) }

ClassDeclaration

объявление класса (не выражение класса)

interface ClassDeclaration {
    type: 'ClassDeclaration';
    id: Identifier | null;
    superClass: Identifier | null;
    body: ClassBody;
}

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

interface ClassBody {
    type: 'ClassBody';
    body: MethodDefinition[];
}

MethodDefinitionпредставляет объявление метода;

interface MethodDefinition {
    type: 'MethodDefinition';
    key: Expression | null;
    computed: boolean;
    value: FunctionExpression | null;
    kind: 'method' | 'constructor';
    static: boolean;
}
class foo {
    constructor() {}
    method() {}
};

ContinueStatement

продолжить заявление

interface ContinueStatement {
    type: 'ContinueStatement';
    label: Identifier | null;
}

Например:

for (var i = 0; i < 10; i++) {
    if (i === 0) {
        continue;
    }
}

DebuggerStatement

инструкция отладчика

interface DebuggerStatement {
    type: 'DebuggerStatement';
}

Например

while(true) {
    debugger;
}

DoWhileStatement

оператор do-while

interface DoWhileStatement {
    type: 'DoWhileStatement';
    body: Statement;
    test: Expression;
}

testПредставляет некоторое условие

Например:

var i = 0;
do {
    i++;
} while(i = 2)

EmptyStatement

пустой оператор

interface EmptyStatement {
    type: 'EmptyStatement';
}

Например:

if(true);

var a = [];
for(i = 0; i < a.length; a[i++] = 0);

ExpressionStatement

Оператор-выражение, то есть оператор, состоящий из одного выражения.

interface ExpressionStatement {
    type: 'ExpressionStatement';
    expression: Expression;
    directive?: string;
}

Когда оператор выражения представляет директиву (например, «use strict»), атрибут директивы будет содержать строку директивы.

Например:

(function(){});

ForStatement

для заявления

interface ForStatement {
    type: 'ForStatement';
    init: Expression | VariableDeclaration | null;
    test: Expression | null;
    update: Expression | null;
    body: Statement;
}

ForInStatement

для ... в заявлении

interface ForInStatement {
    type: 'ForInStatement';
    left: Expression;
    right: Expression;
    body: Statement;
    each: false;
}

ForOfStatement

для... заявления

interface ForOfStatement {
    type: 'ForOfStatement';
    left: Expression;
    right: Expression;
    body: Statement;
}

IfStatement

если оператор

interface IfStatement {
    type: 'IfStatement';
    test: Expression;
    consequent: Statement;
    alternate?: Statement;
}

consequentУказывает содержимое после удара if,alternateУказывает содержимое else или else if.

LabeledStatement

Оператор label в основном используется для точного использования continue и break во вложенных циклах.

interface LabeledStatement {
    type: 'LabeledStatement';
    label: Identifier;
    body: Statement;
}

как:

var num = 0;
outPoint:
for (var i = 0 ; i < 10 ; i++){
        for (var j = 0 ; j < 10 ; j++){
            if( i == 5 && j == 5 ){
                break outPoint;
            }
            num++;
        }
}

ReturnStatement

оператор возврата

interface ReturnStatement {
    type: 'ReturnStatement';
    argument: Expression | null;
}

SwitchStatement

Оператор переключения

interface SwitchStatement {
    type: 'SwitchStatement';
    discriminant: Expression;
    cases: SwitchCase[];
}

discriminantПеременная, представляющая переключатель.

SwitchCaseТипы следующие

interface SwitchCase {
    type: 'SwitchCase';
    test: Expression | null;
    consequent: Statement[];
}

ThrowStatement

заявление о броске

interface ThrowStatement {
    type: 'ThrowStatement';
    argument: Expression;
}

TryStatement

оператор try...catch

interface TryStatement {
    type: 'TryStatement';
    block: BlockStatement;
    handler: CatchClause | null;
    finalizer: BlockStatement | null;
}

handlerСодержимое объявления процесса для catch,finalizerдля окончательного содержания.

CatchClausТипы следующие

interface CatchClause {
    type: 'CatchClause';
    param: Identifier | BindingPattern;
    body: BlockStatement;
}

Например:

try {
    foo();
} catch (e) {
    console.erroe(e);
} finally {
    bar();
}

WhileStatement

пока заявление

interface WhileStatement {
    type: 'WhileStatement';
    test: Expression;
    body: Statement;
}

testявляется выражением решения

WithStatement

с оператором (с указанием области действия оператора блока)

interface WithStatement {
    type: 'WithStatement';
    object: Expression;
    body: Statement;
}

как:

var a = {};

with(a) {
    name = 'xiao.ming';
}

console.log(a); // {name: 'xiao.ming'}

Expressions and Patterns

ExpressionsДоступные типы:

type Expression = ThisExpression | Identifier | Literal |
    ArrayExpression | ObjectExpression | FunctionExpression | ArrowFunctionExpression | ClassExpression |
    TaggedTemplateExpression | MemberExpression | Super | MetaProperty |
    NewExpression | CallExpression | UpdateExpression | AwaitExpression | UnaryExpression |
    BinaryExpression | LogicalExpression | ConditionalExpression |
    YieldExpression | AssignmentExpression | SequenceExpression;

PatternsДоступны два типа: функциональный режим и объектный режим:

type BindingPattern = ArrayPattern | ObjectPattern;

ThisExpression

thisвыражение

interface ThisExpression {
    type: 'ThisExpression';
}

Identifier

Идентификаторы — это имена, которые мы настраиваем при написании JS, такие как имена переменных, имена функций и имена атрибутов, которые классифицируются как идентификаторы. Соответствующий интерфейс выглядит следующим образом:

interface Identifier {
    type: 'Identifier';
    name: string;
}

Literal

Литералы, а не [] или {} здесь, но литералы, которые семантически представляют значение, такие как 1, "hello", true и регулярные выражения (есть расширенный узел для представления регулярных выражений) выражение), например /\d ?/.

interface Literal {
    type: 'Literal';
    value: boolean | number | string | RegExp | null;
    raw: string;
    regex?: { pattern: string, flags: string };
}

Например:

var a = 1;
var b = 'b';
var c = false;
var d = /\d/;

ArrayExpression

выражение массива

interface ArrayExpression {
    type: 'ArrayExpression';
    elements: ArrayExpressionElement[];
}

пример:

[1, 2, 3, 4];

ArrayExpressionElement

Узел выражения массива, тип следующий

type ArrayExpressionElement = Expression | SpreadElement;

Expression содержит все выражения, а SpreadElement — это синтаксис оператора расширения.

SpreadElement

спред оператор

interface SpreadElement {
    type: 'SpreadElement';
    argument: Expression;
}

как:

var a = [3, 4];
var b = [1, 2, ...a];

var c = {foo: 1};
var b = {bar: 2, ...c};

ObjectExpression

объектное выражение

interface ObjectExpression {
    type: 'ObjectExpression';
    properties: Property[];
}

PropertyПредставляет описание свойства объекта

Типы следующие

interface Property {
    type: 'Property';
    key: Expression;
    computed: boolean;
    value: Expression | null;
    kind: 'get' | 'set' | 'init';
    method: false;
    shorthand: boolean;
}

kindИспользуется для обозначения нормальной инициализации или получения/установки.

Например:

var obj = {
    foo: 'foo',
    bar: function() {},
    noop() {}, // method 为 true
    ['computed']: 'computed'  // computed 为 true
}

FunctionExpression

функциональное выражение

interface FunctionExpression {
    type: 'FunctionExpression';
    id: Identifier | null;
    params: FunctionParameter[];
    body: BlockStatement;
    generator: boolean;
    async: boolean;
    expression: boolean;
}

Например:

var foo = function () {}

ArrowFunctionExpression

выражение стрелочной функции

interface ArrowFunctionExpression {
    type: 'ArrowFunctionExpression';
    id: Identifier | null;
    params: FunctionParameter[];
    body: BlockStatement | Expression;
    generator: boolean;
    async: boolean;
    expression: false;
}

generatorУказывает, является ли функция генератором,asyncУказывает, является ли это функцией async/await,paramsОпределяется для параметров.

FunctionParameterТипы следующие

type FunctionParameter = AssignmentPattern | Identifier | BindingPattern;

пример:

var foo = () => {};

ClassExpression

выражение класса

interface ClassExpression {
    type: 'ClassExpression';
    id: Identifier | null;
    superClass: Identifier | null;
    body: ClassBody;
}

Например:

var foo = class {
    constructor() {}
    method() {}
};

TaggedTemplateExpression

литеральная функция шаблона разметки

interface TaggedTemplateExpression {
    type: 'TaggedTemplateExpression';
    readonly tag: Expression;
    readonly quasi: TemplateLiteral;
}

TemplateLiteralТипы следующие

interface TemplateLiteral {
    type: 'TemplateLiteral';
    quasis: TemplateElement[];
    expressions: Expression[];
}

TemplateElementТипы следующие

interface TemplateElement {
    type: 'TemplateElement';
    value: { cooked: string; raw: string };
    tail: boolean;
}

Например

var foo = function(a){ console.log(a); }
foo`test`;

MemberExpression

выражение члена свойства

interface MemberExpression {
    type: 'MemberExpression';
    computed: boolean;
    object: Expression;
    property: Expression;
}

Например:

const foo = {bar: 'bar'};
foo.bar;
foo['bar']; // computed 为 true

Super

родительское ключевое слово

interface Super {
    type: 'Super';
}

Например:

class foo {};
class bar extends foo {
    constructor() {
        super();
    }
}

MetaProperty

(не знаю для чего это)

interface MetaProperty {
    type: 'MetaProperty';
    meta: Identifier;
    property: Identifier;
}

Например:

new.target  // 通过new 声明的对象,new.target会存在

import.meta

CallExpression

выражение выполнения функции

interface CallExpression {
    type: 'CallExpression';
    callee: Expression | Import;
    arguments: ArgumentListElement[];
}

Типа импорт, не понял.

interface Import {
    type: 'Import'
}

ArgumentListElementтип

type ArgumentListElement = Expression | SpreadElement;

как:

var foo = function (){};
foo();

NewExpression

новое выражение

interface NewExpression {
    type: 'NewExpression';
    callee: Expression;
    arguments: ArgumentListElement[];
}

UpdateExpression

выражения оператора обновления, такие как++,--;

interface UpdateExpression {
  type: "UpdateExpression";
  operator: '++' | '--';
  argument: Expression;
  prefix: boolean;
}

как:

var i = 0;
i++;
++i; // prefix为true

AwaitExpression

выражение ожидания, будет использоваться в сочетании с асинхронным.

interface AwaitExpression {
    type: 'AwaitExpression';
    argument: Expression;
}

как

async function foo() {
    var bar = function() {
        new Primise(function(resolve, reject) {
            setTimeout(function() {
                resove('foo')
            }, 1000);
        });
    }
    return await bar();
}

foo() // foo

UnaryExpression

выражение унарного оператора

interface UnaryExpression {
  type: "UnaryExpression";
  operator: UnaryOperator;
  prefix: boolean;
  argument: Expression;
}

перечислитьUnaryOperator

enum UnaryOperator {
  "-" | "+" | "!" | "~" | "typeof" | "void" | "delete" | "throw"
}

BinaryExpression

выражения бинарного оператора

interface BinaryExpression {
    type: 'BinaryExpression';
    operator: BinaryOperator;
    left: Expression;
    right: Expression;
}

перечислитьBinaryOperator

enum BinaryOperator {
  "==" | "!=" | "===" | "!=="
     | "<" | "<=" | ">" | ">="
     | "<<" | ">>" | ">>>"
     | "+" | "-" | "*" | "/" | "%"
     | "**" | "|" | "^" | "&" | "in"
     | "instanceof"
     | "|>"
}

LogicalExpression

выражение логического оператора

interface LogicalExpression {
    type: 'LogicalExpression';
    operator: '||' | '&&';
    left: Expression;
    right: Expression;
}

как:

var a = '-';
var b = a || '-';

if (a && b) {}

ConditionalExpression

условный оператор

interface ConditionalExpression {
    type: 'ConditionalExpression';
    test: Expression;
    consequent: Expression;
    alternate: Expression;
}

Например:

var a = true;
var b = a ? 'consequent' : 'alternate';

YieldExpression

выражение доходности

interface YieldExpression {
    type: 'YieldExpression';
    argument: Expression | null;
    delegate: boolean;
}

Например:

function* gen(x) {
  var y = yield x + 2;
  return y;
}

AssignmentExpression

выражение присваивания.

interface AssignmentExpression {
    type: 'AssignmentExpression';
    operator: '=' | '*=' | '**=' | '/=' | '%=' | '+=' | '-=' |
        '<<=' | '>>=' | '>>>=' | '&=' | '^=' | '|=';
    left: Expression;
    right: Expression;
}

operatorсвойство представляет оператор присваивания,leftиrightэто выражение вокруг оператора присваивания.

SequenceExpression

Выражения последовательности (с использованием запятых).

interface SequenceExpression {
    type: 'SequenceExpression';
    expressions: Expression[];
}
var a, b;
a = 1, b = 2

ArrayPattern

режим разбора массива

interface ArrayPattern {
    type: 'ArrayPattern';
    elements: ArrayPatternElement[];
}

пример:

const [a, b] = [1,3];

elements представляет узел массива

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

type ArrayPatternElement = AssignmentPattern | Identifier | BindingPattern | RestElement | null;

AssignmentPattern

Режим назначения по умолчанию, используемый для синтаксического анализа массива, синтаксического анализа объектов и значений параметров функции по умолчанию.

interface AssignmentPattern {
    type: 'AssignmentPattern';
    left: Identifier | BindingPattern;
    right: Expression;
}

пример:

const [a, b = 4] = [1,3];

RestElement

Режим остальных параметров, синтаксис аналогичен оператору расширения.

interface RestElement {
    type: 'RestElement';
    argument: Identifier | BindingPattern;
}

пример:

const [a, b, ...c] = [1, 2, 3, 4];

ObjectPatterns

Режим разрешения объекта

interface ObjectPattern {
    type: 'ObjectPattern';
    properties: Property[];
}

пример:

const object = {a: 1, b: 2};
const { a, b } = object;

Заканчивать

Роль AST можно условно разделить на несколько категорий.

  1. Использование IDE, например определение стиля кода (eslint и т. д.), форматирование кода, подсветка кода, ошибки в коде и т. д.

  2. обфускация кода

  3. Инструменты для преобразования кода. Такие как webpack, rollup, преобразование между различными спецификациями кода, ts, jsx и т. д. преобразуются в собственный js.

Понимание AST в конечном счете связано с пониманием инструментов, которые мы используем, и, конечно же, приближением нас к JavaScript.

использованная литература