предисловие
В настоящее время 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 можно условно разделить на несколько категорий.
-
Использование IDE, например определение стиля кода (eslint и т. д.), форматирование кода, подсветка кода, ошибки в коде и т. д.
-
обфускация кода
-
Инструменты для преобразования кода. Такие как webpack, rollup, преобразование между различными спецификациями кода, ts, jsx и т. д. преобразуются в собственный js.
Понимание AST в конечном счете связано с пониманием инструментов, которые мы используем, и, конечно же, приближением нас к JavaScript.