Skip to content

Instantly share code, notes, and snippets.

@fand
Last active August 29, 2015 14:24
Show Gist options
  • Save fand/92a8d222058168773179 to your computer and use it in GitHub Desktop.
Save fand/92a8d222058168773179 to your computer and use it in GitHub Desktop.
Esprima Syntax メモ

かならず Program で始まる

Program

  • type
  • body: [Statement]
    • ExpressionStatement, VariableDeclaration, EmptyStatement, DebuggerStatement, TryStatement が入る?
{ type: 'Program',
  body: [ { type: 'ExpressionStatement', expression: [Object] } ] }

bodyはExpressionStatementの配列

AssignmentExpression

代入

a *= 1;

{ type: 'AssignmentExpression',
  operator: '*=',
  left: { type: 'Identifier', name: 'a' },
  right: { type: 'Literal', value: 1, raw: '1' } }

AssignmentPattern (ES6)

ObjectPattern や ArrayPattern の中に代入式があると AssignmentPattern になる……???

> esprima.parse('[a=0] = [1]').body[0].expression.left
{ type: 'ArrayPattern',
  elements:
   [ { type: 'AssignmentPattern',
       operator: '=',
       left: [Object],
       right: [Object] } ] }
> esprima.parse('[a=0] = [1]').body[0].expression.left.elements[0]
{ type: 'AssignmentPattern',
  operator: '=',
  left: { type: 'Identifier', name: 'a' },
  right: { type: 'Literal', value: 0, raw: '0' } }

ArrayExpression

> esprima.parse('[1]').body[0].expression
{ type: 'ArrayExpression',
  elements: [ { type: 'Literal', value: 1, raw: '1' } ] }

ArrayPattern (ES6)

> e.parse('let [a,] = 0').body[0]

{ type: 'VariableDeclaration',
  declarations: [ { type: 'VariableDeclarator', id: [Object], init: [Object] } ],
  kind: 'let' }

ArrowFunctionExpression (ES6)

> esprima.parse('() => 1').body[0].expression
{ type: 'ArrowFunctionExpression',
  id: null,
  params: [],
  defaults: [],
  body: { type: 'Literal', value: 1, raw: '1' },
  generator: false,
  expression: true }

BlockStatement

body : [ExpressionStatement] を持つ

> esprima.parse('{1}').body[0]
{ type: 'BlockStatement',
  body: [ { type: 'ExpressionStatement', expression: [Object] } ] }

BinaryExpression

バイナリ演算

> esprima.parse('0b11 & 0b01').body[0]
{ type: 'ExpressionStatement',
  expression:
   { type: 'BinaryExpression',
     operator: '&',
     left: { type: 'Literal', value: 3, raw: '0b11' },
     right: { type: 'Literal', value: 1, raw: '0b01' } } }

BreakStatement

ループのbreak

> esprima.parse('while(1){ break;}').body[0]
{ type: 'WhileStatement',
  test: { type: 'Literal', value: 1, raw: '1' },
  body: {
    type: 'BlockStatement', body: [ {
      type: 'BreakStatement', label: null
    } ]
  }
}

CallExpression

関数呼び出し

> esprima.parse('a()').body[0]
{ type: 'ExpressionStatement',
  expression:
   { type: 'CallExpression',
     callee: { type: 'Identifier', name: 'a' },
     arguments: [] } }

CatchClause

> esprima.parse('try {} catch(e) {}').body[0]
{ type: 'TryStatement',
  block: { type: 'BlockStatement', body: [] },
  guardedHandlers: [],
  handlers: [ { type: 'CatchClause', param: [Object], body: [Object] } ],
  handler:
   { type: 'CatchClause',
     param: { type: 'Identifier', name: 'e' },
     body: { type: 'BlockStatement', body: [] } },
  finalizer: null }

ClassBody (ES6)

  • type
  • body : [MethodDefinition]
> esprima.parse('class A extends B { constructor(){} yo () {} static go(){} }').body[0].body

{ type: 'ClassBody',
  body:
   [ { type: 'MethodDefinition',
       key: [Object],
       computed: false,
       value: [Object],
       kind: 'constructor',
       static: false },
     { type: 'MethodDefinition',
       key: [Object],
       computed: false,
       value: [Object],
       kind: 'method',
       static: false },
     { type: 'MethodDefinition',
       key: [Object],
       computed: false,
       value: [Object],
       kind: 'method',
       static: true } ] }

ClassDeclaration (ES6)

  • type
  • id
  • superClass
  • body (ClassBody)
> esprima.parse('class A extends B { constructor(){} yo () {} static go(){} }').body[0]

{ type: 'ClassDeclaration',
  id: { type: 'Identifier', name: 'A' },
  superClass: { type: 'Identifier', name: 'B' },
  body: { type: 'ClassBody', body: [ [Object], [Object], [Object] ] } }

ClassExpression (ES6)

ClassDeclarationの式バージョン

> esprima.parse('a = class A {}').body[0]

{ type: 'ExpressionStatement',
  expression:
   { type: 'AssignmentExpression',
     operator: '=',
     left: { type: 'Identifier', name: 'a' },
     right:
      { type: 'ClassExpression',
        id: [Object],
        superClass: null,
        body: [Object] } } }

ConditionalExpression

三項演算子

true ? 1 : 2

{ type: 'ConditionalExpression',
     test: { type: 'Literal', value: true, raw: 'true' },
     consequent: { type: 'Literal', value: 1, raw: '1' },
     alternate: { type: 'Literal', value: 2, raw: '2' } }

ContinueStatement

  • type
  • label (LabeledStatementの名前)
> esprima.parse('while(1){ continue; }').body[0]
{ type: 'WhileStatement',
  test: { type: 'Literal', value: 1, raw: '1' },
  body: { type: 'BlockStatement', body: [ [Object] ] } }

> esprima.parse('while(1){ continue; }').body[0].body
{ type: 'BlockStatement',
  body: [ { type: 'ContinueStatement', label: null } ] }

DoWhileStatement

> esprima.parse('do{}while(1)').body[0]
{ type: 'DoWhileStatement',
  body: { type: 'BlockStatement', body: [] },
  test: { type: 'Literal', value: 1, raw: '1' } }

DebuggerStatement

> esprima.parse('debugger;').body[0]
{ type: 'DebuggerStatement' }

EmptyStatement

> esprima.parse(';').body[0]
{ type: 'EmptyStatement' }

ExportAllDeclaration (ES6)

> esprima.parse('export * from "foo";', {sourceType: 'module'}).body[0];
{ type: 'ExportAllDeclaration',
  source: { type: 'Literal', value: 'foo', raw: '"foo"' } }

ExportDefaultDeclaration (ES6)

{ type: 'ExportDefaultDeclaration',
  declaration: { type: 'Literal', value: 123, raw: '123' } }

ExportNamedDeclaration (ES6)

> esprima.parse('export const A = 123', {sourceType: 'module'}).body[0];

{ type: 'ExportNamedDeclaration',
  declaration:
   { type: 'VariableDeclaration',
     declarations: [ [Object] ],
     kind: 'const' },
  specifiers: [],
  source: null }

> esprima.parse('export const A = 123', {sourceType: 'module'}).body[0].declaration;

{ type: 'VariableDeclaration',
  declarations: [ { type: 'VariableDeclarator', id: [Object], init: [Object] } ],
  kind: 'const' }

ExportSpecifier (ES6)

as で名前指定してexportするやつ

> esprima.parse('export {a, b}', {sourceType: 'module'}).body[0];
{ type: 'ExportNamedDeclaration',
  declaration: null,
  specifiers:
   [ { type: 'ExportSpecifier', exported: [Object], local: [Object] },
     { type: 'ExportSpecifier', exported: [Object], local: [Object] } ],
  source: null }
> esprima.parse('export {_A as A}', {sourceType: 'module'}).body[0];
{ type: 'ExportNamedDeclaration',
  declaration: null,
  specifiers: [ { type: 'ExportSpecifier', exported: [Object], local: [Object] } ],
  source: null }
> esprima.parse('export {_A as A}', {sourceType: 'module'}).body[0].specifiers[0];
{ type: 'ExportSpecifier',
  exported: { type: 'Identifier', name: 'A' },
  local: { type: 'Identifier', name: '_A' } }

ExpressionStatement

  • type
  • expression:

1

{ type: 'ExpressionStatement',
    expression: { type: 'Literal', value: 1, raw: '1' } }

ForStatement

  • init, test, update : 式
  • body : 文
> esprima.parse('for (1;2;3) { 1; }').body
[ { type: 'ForStatement',
    init: { type: 'Literal', value: 1, raw: '1' },
    test: { type: 'Literal', value: 2, raw: '2' },
    update: { type: 'Literal', value: 3, raw: '3' },
    body: { type: 'BlockStatement', body: [Object] } } ]

> esprima.parse('for (let i = 0; i < 10; i++) a[i] = i;').body
[ { type: 'ForStatement',
    init:
     { type: 'VariableDeclaration',
       declarations: [Object],
       kind: 'let' },
    test:
     { type: 'BinaryExpression',
       operator: '<',
       left: [Object],
       right: [Object] },
    update:
     { type: 'UpdateExpression',
       operator: '++',
       argument: [Object],
       prefix: false },
    body: { type: 'ExpressionStatement', expression: [Object] } } ]

ForOfStatement

省略

ForInStatement

省略

FunctionDeclaration

関数宣言の文 暗黙的にグローバル変数となる

function f(){}

{ type: 'FunctionDeclaration',
  id: { type: 'Identifier', name: 'f' },
  params: [],
  defaults: [],
  body: { type: 'BlockStatement', body: [] },
  generator: false,
  expression: false }

FunctionExpression

関数式

var g = function f(){}

{ type: 'VariableDeclaration',
  declarations: [ {
    type: 'VariableDeclarator',
    id: { type: 'Identifier', name: 'g' }
    init: { type: 'FunctionExpression',
     id: { type: 'Identifier', name: 'f' },
     params: [],
     defaults: [],
     body: { type: 'BlockStatement', body: [] },
     generator: false,
     expression: false }
  } ],
  kind: 'var' }

Identifier

識別子

a

{ type: 'Identifier', name: 'a' }

IfStatement

  • test : 式
  • consequent, alternate : 文
> esprima.parse('if (1) 1; else 2;').body
[ { type: 'IfStatement',
    test: { type: 'Literal', value: 1, raw: '1' },
    consequent: { type: 'ExpressionStatement', expression: [Object] },
    alternate: { type: 'ExpressionStatement', expression: [Object] } } ]

ImportDeclaration

  • type
  • specifiers : [ImportDefaultSpecifier]
  • source
> esprima.parse('import a from "A"', {sourceType: 'module'}).body[0];
{ type: 'ImportDeclaration',
  specifiers: [ { type: 'ImportDefaultSpecifier', local: [Object] } ],
  source: { type: 'Literal', value: 'A', raw: '"A"' } }

> esprima.parse('import * as AAA from "A"', {sourceType: 'module'}).body[0];
{ type: 'ImportDeclaration',
  specifiers: [ { type: 'ImportNamespaceSpecifier', local: [Object] } ],
  source: { type: 'Literal', value: 'A', raw: '"A"' } }

ImportDefaultSpecifier

> esprima.parse('import a from "A"', {sourceType: 'module'}).body[0].specifiers;
[ { type: 'ImportDefaultSpecifier',
    local: { type: 'Identifier', name: 'a' } } ]

ImportNamespaceSpecifier

> esprima.parse('import * as AAA from "A"', {sourceType: 'module'}).body[0].specifiers[0];
{ type: 'ImportNamespaceSpecifier',
  local: { type: 'Identifier', name: 'AAA' } }

ImportSpecifier

> esprima.parse('import {a as A, b as B} from "A"', {sourceType: 'module'}).body[0].specifiers
[ { type: 'ImportSpecifier',
    local: { type: 'Identifier', name: 'A' },
    imported: { type: 'Identifier', name: 'a' } },
  { type: 'ImportSpecifier',
    local: { type: 'Identifier', name: 'B' },
    imported: { type: 'Identifier', name: 'b' } } ]

Literal

"hoi"

{ type: 'Literal', value: 'hoi', raw: '"hoi"' }

3

{ type: 'Literal', value: 3, raw: '3' }

LabeledStatement

breakやcontinueで使う

> esprima.parse('yo: 1;').body
[ { type: 'LabeledStatement',
    label: { type: 'Identifier', name: 'yo' },
    body: { type: 'ExpressionStatement', expression: [Object] } } ]
> esprima.parse('a: while(1){ b:while(1){ break b; } break a; }').body
[ { type: 'LabeledStatement',
    label: { type: 'Identifier', name: 'a' },
    body: { type: 'WhileStatement', test: [Object], body: [Object] } } ]

実行例

> a: while(1){ b:while(1){ console.log('b');break b; } console.log('a');break a; }
b
a
undefined

LogicalExpression

論理式

> esprima.parse('a || b').body
[ { type: 'ExpressionStatement',
    expression:
     { type: 'LogicalExpression',
       operator: '||',
       left: [Object],
       right: [Object] } } ]

MemberExpression

メンバー演算子 object . property という形式

a.b

{ type: 'MemberExpression',
     computed: false,
     object: { type: 'Identifier', name: 'a' },
     property: { type: 'Identifier', name: 'b' } }

MetaProperty (ES6)

new.targetのことっぽい 2.4.1だとまだ未実装 http://www.2ality.com/2015/02/es6-classes-final.html

MethodDefinition (ES6)

クラスのメソッド定義

> esprima.parse('class A{ x () {1;} }').body[0];
{ type: 'ClassDeclaration',
  id: { type: 'Identifier', name: 'A' },
  superClass: null,
  body: { type: 'ClassBody', body: [ [Object] ] } }

> esprima.parse('class A{ x (a) {1;} }').body[0].body.body
[ { type: 'MethodDefinition',
    key: { type: 'Identifier', name: 'x' },
    computed: false,
    value:
     { type: 'FunctionExpression',
       id: null,
       params: [Object],
       defaults: [],
       body: [Object],
       generator: false,
       expression: false },
    kind: 'method',
    static: false } ]

NewExpression

new Anew A() で違いは無いのか、マジか

> esprima.parse('new A').body[0]
{ type: 'ExpressionStatement',
  expression:
   { type: 'NewExpression',
     callee: { type: 'Identifier', name: 'A' },
     arguments: [] } }

> esprima.parse('new A()').body[0]
{ type: 'ExpressionStatement',
  expression:
   { type: 'NewExpression',
     callee: { type: 'Identifier', name: 'A' },
     arguments: [] } }

> esprima.parse('new A(1)').body[0]
{ type: 'ExpressionStatement',
  expression:
   { type: 'NewExpression',
     callee: { type: 'Identifier', name: 'A' },
     arguments: [ [Object] ] } }

> esprima.parse('new A(1)').body[0].expression.arguments
[ { type: 'Literal', value: 1, raw: '1' } ]

ObjectExpression

Objectの式

  • type
  • properties : [Property]
> esprima.parse('{a:1}').body[0]
{ type: 'BlockStatement',
  body: [ { type: 'LabeledStatement', label: [Object], body: [Object] } ] }

> esprima.parse('a = {a:1}').body[0]
{ type: 'ExpressionStatement',
  expression:
   { type: 'AssignmentExpression',
     operator: '=',
     left: { type: 'Identifier', name: 'a' },
     right: { type: 'ObjectExpression', properties: [Object] } } }

> esprima.parse('a = {a:1}').body[0].expression.right
{ type: 'ObjectExpression',
  properties:
   [ { type: 'Property',
       key: [Object],
       computed: false,
       value: [Object],
       kind: 'init',
       method: false,
       shorthand: false } ] }

1個目はオブジェクトじゃないのか〜

ObjectPattern (ES6)

> esprima.parse('var { m: month, y: year } = today();').body[0].declarations[0].id;
{ type: 'ObjectPattern',
  properties:
   [ { type: 'Property',
       key: [Object],
       computed: false,
       value: [Object],
       kind: 'init',
       method: false,
       shorthand: false },
     { type: 'Property',
       key: [Object],
       computed: false,
       value: [Object],
       kind: 'init',
       method: false,
       shorthand: false } ] }

Property

Object定義する時の、一つ一つのプロパティ

> esprima.parse('a = {a:1}').body[0].expression.right.properties[0]
{ type: 'Property',
  key: { type: 'Identifier', name: 'a' },
  computed: false,
  value: { type: 'Literal', value: 1, raw: '1' },
  kind: 'init',
  method: false,
  shorthand: false }

RestElement (ES6)

> esprima.parse('function f (a, ...b){ }').body[0]
{ type: 'FunctionDeclaration',
  id: { type: 'Identifier', name: 'f' },
  params:
   [ { type: 'Identifier', name: 'a' },
     { type: 'RestElement', argument: [Object] } ],
  defaults: [],
  body: { type: 'BlockStatement', body: [] },
  generator: false,
  expression: false }

> esprima.parse('function f (a, ...b){ }').body[0].params[1]
{ type: 'RestElement',
  argument: { type: 'Identifier', name: 'b' } }

ReturnStatement

> esprima.parse('function f(){return 1;}').body[0].body.body[0]
{ type: 'ReturnStatement',
  argument: { type: 'Literal', value: 1, raw: '1' } }

SequenceExpression

, 演算子?で式を並べた時、式の集合を SequenceExpression と呼ぶ

> esprima.parse('a,b').body[0]
{ type: 'ExpressionStatement',
  expression:
   { type: 'SequenceExpression',
     expressions: [ [Object], [Object] ] } }

この時、最後の式の値が返る 実行例

> a = (1,2,3);
3

SpreadElement (ES6)

> esprima.parse('a(...b)').body[0]
{ type: 'ExpressionStatement',
  expression:
   { type: 'CallExpression',
     callee: { type: 'Identifier', name: 'a' },
     arguments: [ [Object] ] } }

> esprima.parse('a(...b)').body[0].expression.arguments
[ { type: 'SpreadElement',
    argument: { type: 'Identifier', name: 'b' } } ]

Super (ES6)

クラス定義内の super .

> esprima.parse('class A { yo(){ super(); } }').body[0].body.body[0].value.body.body[0].expression
{ type: 'CallExpression',
  callee: { type: 'Super' },
  arguments: [] } }

SwitchCase

> esprima.parse('switch(a){ case 1: 1; default: 0; }').body[0].cases[0]
{ type: 'SwitchCase',
  test: { type: 'Literal', value: 1, raw: '1' },
  consequent: [ { type: 'ExpressionStatement', expression: [Object] } ] }

SwitchStatement

> esprima.parse('switch(a){}').body[0]
{ type: 'SwitchStatement',
  discriminant: { type: 'Identifier', name: 'a' },
  cases: [] }

> esprima.parse('switch(a){ case 1: 1; default: 0; }').body[0]
{ type: 'SwitchStatement',
  discriminant: { type: 'Identifier', name: 'a' },
  cases:
   [ { type: 'SwitchCase', test: [Object], consequent: [Object] },
     { type: 'SwitchCase', test: null, consequent: [Object] } ] }  

TaggedTemplateExpression

> esprima.parse('raw`42`').body[0].expression
{ type: 'TaggedTemplateExpression',
  tag: { type: 'Identifier', name: 'raw' },
  quasi:
    { type: 'TemplateLiteral',
      quasis: [
        { type: 'TemplateElement', value: [Object], tail: true }
      ],
      expressions: [] } }

TemplateLiteral

TemplateElement

> esprima.parse('`hello ${a}, wassa`').body[0].expression
{ type: 'TemplateLiteral',
  quasis:
    [ {
      type: 'TemplateElement',
      value: { raw: 'hello ', cooked: 'hello ' },
      tail: false
    }, {
      type: 'TemplateElement',
      value: { raw: ', wassa', cooked: ', wassa' },
      tail: true
    } ],
  expressions: [ { type: 'Identifier', name: 'a' } ] }

ThisExpression

this

{ type: 'ThisExpression' },

ThrowStatement

> esprima.parse('throw "yo"').body[0]
{ type: 'ThrowStatement',
  argument: { type: 'Literal', value: 'yo', raw: '"yo"' } }

TryStatement

> esprima.parse('try {1} catch(e) {2} finally {3}').body[0]
{ type: 'TryStatement',
  block: { type: 'BlockStatement', body: [ [Object] ] },
  guardedHandlers: [],
  handlers: [ { type: 'CatchClause', param: [Object], body: [Object] } ],
  handler:
   { type: 'CatchClause',
     param: { type: 'Identifier', name: 'e' },
     body: { type: 'BlockStatement', body: [Object] } },
  finalizer: { type: 'BlockStatement', body: [ [Object] ] } }

catch, finally は try の子供になるのか〜

UnaryExpression

単項演算子

> esprima.parse('!a').body[0]
{ type: 'ExpressionStatement',
  expression:
   { type: 'UnaryExpression',
     operator: '!',
     argument: { type: 'Identifier', name: 'a' },
     prefix: true } }

UpdateExpression

> esprima.parse('++a').body[0]
{ type: 'ExpressionStatement',
  expression:
   { type: 'UpdateExpression',
     operator: '++',
     argument: { type: 'Identifier', name: 'a' },
     prefix: true } }

VariableDeclarator

> esprima.parse('var a = 1, b = 2;').body[0].declarations[0]
{ type: 'VariableDeclarator',
  id: { type: 'Identifier', name: 'a' },
  init: { type: 'Literal', value: 1, raw: '1' } }

VariableDeclaration

  • type
  • declarations : [VariableDeclarator]
  • kind : 'var' | 'let' | 'const'
> esprima.parse('var a = 1, b = 2;').body[0]
{ type: 'VariableDeclaration',
  declarations:
   [ { type: 'VariableDeclarator', id: [Object], init: [Object] },
     { type: 'VariableDeclarator', id: [Object], init: [Object] } ],
  kind: 'var' }

WhileStatement

> esprima.parse('while(3)1;').body[0]
{ type: 'WhileStatement',
  test: { type: 'Literal', value: 3, raw: '3' },
  body:
   { type: 'ExpressionStatement',
     expression: { type: 'Literal', value: 1, raw: '1' } } }

WithStatement

> esprima.parse('with (console) log("yo"); ').body[0]
{ type: 'WithStatement',
  object: { type: 'Identifier', name: 'console' },
  body:
   { type: 'ExpressionStatement',
     expression: { type: 'CallExpression', callee: [Object], arguments: [Object] } } }

YieldExpression (ES6)

> esprima.parse('function* g(){ yield 1; }').body[0].body.body[0].expression
{ type: 'YieldExpression',
  argument: { type: 'Literal', value: 1, raw: '1' },
  delegate: false }

> esprima.parse('function* g(){ yield* g2; }').body[0].body.body[0].expression
{ type: 'YieldExpression',
  argument: { type: 'Identifier', name: 'g2' },
  delegate: true }

delegate知らなかった、便利

ちなみにgenerator定義はFunctionExpression、FunctionDeclarationである

> esprima.parse('function* g(){ yield 1; }').body[0]
{ type: 'FunctionDeclaration',
  id: { type: 'Identifier', name: 'g' },
  params: [],
  defaults: [],
  body: { type: 'BlockStatement', body: [ [Object] ] },
  generator: true,
  expression: false }

> esprima.parse('f = function*(){ yield 1; }').body[0].expression.right
{ type: 'FunctionExpression',
  id: null,
  params: [],
  defaults: [],
  body: { type: 'BlockStatement', body: [ [Object] ] },
  generator: true,
  expression: false }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment