// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ast.js 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
                                                                                | printWidth
=====================================input======================================
/**
 * @flow
 */

export type InferredType =
  | 'unknown'
  | 'gender'
  | 'enum'
  | 'number-or-string'
  | 'number'
  | 'string'
  | 'error'
;

export type Pos = {
  firstLine: number,
  firstColumn: number,
  lastLine: number,
  lastColumn: number,
};

export type TypedBinaryOpNode = {
  exprNodeType: 'binary_op',
  binaryOp: 'plus' | 'multiply' | 'divide' | 'minus',
  lhs: TypedNode,
  rhs: TypedNode,
  pos: Pos,
  exprType: InferredType,
  typed: true,
}

export type TypedUnaryMinusNode = {
  exprNodeType: 'unary_minus',
  op: TypedNode,
  pos: Pos,
  exprType: InferredType,
  typed: true,
}

export type TypedNumberNode = {
  exprNodeType: 'number',
  value: number,
  pos: Pos,
  exprType: 'number',
  typed: true,
}

export type TypedStringLiteralNode = {
  exprNodeType: 'string_literal',
  value: string,
  pos: Pos,
  exprType: 'string',
  typed: true,
}

export type TypedVariableNode = {
  exprNodeType: 'variable',
  name: string,
  pos: Pos,
  exprType: InferredType,
  typed: true,
};

export type TypedFunctionInvocationNode = {
  exprNodeType: 'function_invocation',
  name: string,
  parameters: TypedNode[],
  pos: Pos,
  exprType: 'error' | 'string',
  typed: true,
}

export type TypedNode =
  | TypedBinaryOpNode
  | TypedUnaryMinusNode
  | TypedNumberNode
  | TypedStringLiteralNode
  | TypedVariableNode
  | TypedFunctionInvocationNode
;

=====================================output=====================================
/**
 * @flow
 */

export type InferredType =
  | "unknown"
  | "gender"
  | "enum"
  | "number-or-string"
  | "number"
  | "string"
  | "error";

export type Pos = {
  firstLine: number,
  firstColumn: number,
  lastLine: number,
  lastColumn: number
};

export type TypedBinaryOpNode = {
  exprNodeType: "binary_op",
  binaryOp: "plus" | "multiply" | "divide" | "minus",
  lhs: TypedNode,
  rhs: TypedNode,
  pos: Pos,
  exprType: InferredType,
  typed: true
};

export type TypedUnaryMinusNode = {
  exprNodeType: "unary_minus",
  op: TypedNode,
  pos: Pos,
  exprType: InferredType,
  typed: true
};

export type TypedNumberNode = {
  exprNodeType: "number",
  value: number,
  pos: Pos,
  exprType: "number",
  typed: true
};

export type TypedStringLiteralNode = {
  exprNodeType: "string_literal",
  value: string,
  pos: Pos,
  exprType: "string",
  typed: true
};

export type TypedVariableNode = {
  exprNodeType: "variable",
  name: string,
  pos: Pos,
  exprType: InferredType,
  typed: true
};

export type TypedFunctionInvocationNode = {
  exprNodeType: "function_invocation",
  name: string,
  parameters: TypedNode[],
  pos: Pos,
  exprType: "error" | "string",
  typed: true
};

export type TypedNode =
  | TypedBinaryOpNode
  | TypedUnaryMinusNode
  | TypedNumberNode
  | TypedStringLiteralNode
  | TypedVariableNode
  | TypedFunctionInvocationNode;

================================================================================
`;

exports[`emit.js 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
                                                                                | printWidth
=====================================input======================================
/**
 * @flow
 */
import * as t from './jsAst';

const b = t.builders;

import type {
        TypedNode
} from './ast';

function getBinaryOp(op: 'plus' | 'minus' | 'divide' | 'multiply') : '+' | '-' | '*' | '/' {
  switch (op) {
  case 'plus':
    return '+';
  case 'minus':
    return '-';
  case 'divide':
    return '/';
  case 'multiply':
    return '*';
  default:
    throw new Error('Invalid binary operator: ' + op);
  }
}

export function emitExpression(node: TypedNode) : t.Expression {
  switch (node.exprNodeType) {
  case 'string_literal': // FALLTHROUGH
  case 'number':
    return b.literal(node.value);
  case 'variable':
    return b.memberExpression(
      b.identifier('vars'),
      b.identifier(node.name),
      false
    );
  case 'binary_op': {
    const lhs = emitExpression(node.lhs);
    const rhs = emitExpression(node.rhs);

    const op = getBinaryOp(node.binaryOp);
    return b.binaryExpression(op, lhs, rhs);
  }
  case 'unary_minus': {
    const operand = emitExpression(node.op);
    return b.unaryExpression('-', operand, true);
  }
  case 'function_invocation': {
    const callee = b.memberExpression(
      b.identifier('fns'),
      b.identifier(node.name),
      false
    );

    const args = node.parameters.map(
      (n) => emitExpression(n)
    );

    return b.callExpression(callee, args);
  }
  default:
    throw new Error('Unknown expression type: ' + node.type);
  }
}

=====================================output=====================================
/**
 * @flow
 */
import * as t from "./jsAst";

const b = t.builders;

import type { TypedNode } from "./ast";

function getBinaryOp(
  op: "plus" | "minus" | "divide" | "multiply"
): "+" | "-" | "*" | "/" {
  switch (op) {
    case "plus":
      return "+";
    case "minus":
      return "-";
    case "divide":
      return "/";
    case "multiply":
      return "*";
    default:
      throw new Error("Invalid binary operator: " + op);
  }
}

export function emitExpression(node: TypedNode): t.Expression {
  switch (node.exprNodeType) {
    case "string_literal": // FALLTHROUGH
    case "number":
      return b.literal(node.value);
    case "variable":
      return b.memberExpression(
        b.identifier("vars"),
        b.identifier(node.name),
        false
      );
    case "binary_op": {
      const lhs = emitExpression(node.lhs);
      const rhs = emitExpression(node.rhs);

      const op = getBinaryOp(node.binaryOp);
      return b.binaryExpression(op, lhs, rhs);
    }
    case "unary_minus": {
      const operand = emitExpression(node.op);
      return b.unaryExpression("-", operand, true);
    }
    case "function_invocation": {
      const callee = b.memberExpression(
        b.identifier("fns"),
        b.identifier(node.name),
        false
      );

      const args = node.parameters.map(n => emitExpression(n));

      return b.callExpression(callee, args);
    }
    default:
      throw new Error("Unknown expression type: " + node.type);
  }
}

================================================================================
`;

exports[`jsAst.js 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
                                                                                | printWidth
=====================================input======================================
/**
 * @flow
 */
export type Comment = {
  loc: ?SourceLocation,
  value: string,
  leading: boolean,
  trailing: boolean,
};

export type SourceLocation = {
  start: SourcePosition,
  end: SourcePosition,
  source: ?string,
};

export type SourcePosition = {
  line: number,
  column: number,
};

export type File = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'File',
  program: Program,
}

export type Program = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'Program',
  body: Statement[],
}

export type BinaryOperator =
  |'=='
  | '!='
  | '==='
  | '!=='
  | '<'
  | '<='
  | '>'
  | '>='
  | '<<'
  | '>>'
  | '>>>'
  | '+'
  | '-'
  | '*'
  | '/'
  | '%'
  | '&' // TODO Missing from the Parser API.
  | '|'
  | '^'
  | 'in'
  | 'instanceof'
  | '..'
;

export type UnaryOperator =
  | '-'
  | '+'
  | '!'
  | '~'
  | 'typeof'
  | 'void'
  | 'delete'
;

export type AssignmentOperator =
  | '='
  | '+='
  | '-='
  | '*='
  | '/='
  | '%='
  | '<<='
  | '>>='
  | '>>>='
  | '|='
  | '^='
  | '&='
;

export type UpdateOperator =
  | '++'
  | '--'
;

export type LogicalOperator =
  | '&&'
  | '||'
;

export type Node =
  | EmptyStatement
  | BlockStatement
  | ExpressionStatement
  | IfStatement
  | BreakStatement
  | ContinueStatement
  | ReturnStatement
  | ThrowStatement
  | WhileStatement
  | ForStatement
  | ForInStatement
  | TryStatement
  | CatchClause
  | Identifier
  | Literal
  | ThisExpression
  | ArrayExpression
  | ObjectExpreession
  | Property
  | FunctionExpression
  | BinaryExpression
  | UnaryExpression
  | AssignmentExpression
  | UpdateExpression
  | LogicalExpression
  | ConditionalExpression
  | NewExpression
  | CallExpression
  | MemberExpression
  | VariableDeclaration
  | FunctionDeclaration
  | VariableDeclarator
;

export type Statement =
  | BlockStatement
  | EmptyStatement
  | ExpressionStatement
  | IfStatement
  | BreakStatement
  | ContinueStatement
  | ReturnStatement
  | ThrowStatement
  | WhileStatement
  | ForStatement
  | ForInStatement
  | TryStatement
  | Declaration
;

export type EmptyStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'EmptyStatement',
}

export type BlockStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'BlockStatement',
  body: Statement[],
}

export type ExpressionStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'ExpressionStatement',
  expression: Expression,
}

export type IfStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'IfStatement',
  test: Expression,
  consequent: Statement,
  alternate: ?Statement,
}

export type BreakStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'BreakStatement',
  label: ?Identifier,
}

export type ContinueStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'ContinueStatement',
  label: ?Identifier,
}

export type ReturnStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'ReturnStatement',
  argument: ?Expression,
}

export type ThrowStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'ThrowStatement',
  argument: ?Expression,
}

export type WhileStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'WhileStatement',
  test: Expression,
  body: Statement,
}

export type ForStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'ForStatement',
  init: ?(VariableDeclaration | Expression),
  test: ?Expression,
  update: ?Expression,
  body: Statement,
}

export type ForInStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'ForInStatement',
  left: VariableDeclaration | Expression,
  right: Expression,
  body: Statement,
}

export type TryStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'TryStatement',
  block: BlockStatement,
  handler: ?CatchClause,
  handlers: CatchClause[],
  finalizer: ?BlockStatement,
};

export type CatchClause = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'CatchClause',
  param: Pattern,
  guard: ?Expression,
  body: BlockStatement,
};

export type Expression =
  | Identifier
  | ThisExpression
  | Literal
  | FunctionExpression
  | BinaryExpression
  | UnaryExpression
  | AssignmentExpression
  | UpdateExpression
  | LogicalExpression
  | ConditionalExpression
  | NewExpression
  | CallExpression
  | MemberExpression
  | ArrayExpression
  | ObjectExpreession
;

export type Identifier = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'Identifier',
  name: string,
}

export type Literal = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'Literal',
  value: ?(string | boolean | number | RegExp),
  regex: ?{ pattern: string, flags: string },
}

export type ThisExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'ThisExpression',
}

export type ArrayExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'ArrayExpression',
  elements: Expression[],
}

export type ObjectExpreession = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'ObjectExpression',
  properties: Property[],
}

export type Property = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'Property',
  kind: 'init' | 'get' | 'set',
  key: Literal | Identifier,
  value: Expression,
};

export type FunctionExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'FunctionExpression',
  id: ?Identifier,
  params: Pattern[],
  body: BlockStatement,
}

export type BinaryExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'BinaryExpression',
  operator: BinaryOperator,
  left: Expression,
  right: Expression,
}

export type UnaryExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'UnaryExpression',
  operator: UnaryOperator,
  argument: Expression,
  prefix: boolean,
};

export type AssignmentExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'AssignmentExpression',
  operator: AssignmentOperator,
  left: Pattern,
  right: Expression,
};

export type UpdateExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'UpdateExpression',
  operator: UpdateOperator,
  argument: Expression,
  prefix: boolean,
};

export type LogicalExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'LogicalExpression',
  operator: LogicalOperator,
  left: Expression,
  right: Expression,
};

export type ConditionalExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'ConditionalExpression',
  test: Expression,
  consequent: Expression,
  alternate: Expression,
};

export type NewExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'NewExpression',
  callee: Expression,
  arguments: Expression[],
};

export type CallExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'CallExpression',
  callee: Expression,
  arguments: Expression[],
};

export type MemberExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'MemberExpression',
  object: Expression,
  property: Identifier | Expression,
  computed: bool,
}
// ast-types exports all expressions as patterns.
// That seems not like it was intended.
export type Pattern =
  | Identifier
;

export type Declaration =
  | VariableDeclaration
  | FunctionDeclaration
;

export type VariableDeclaration = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'VariableDeclaration',
  kind: 'var' | 'let' | 'const',
  declarations: VariableDeclarator[],
}

export type FunctionDeclaration = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'FunctionDeclaration',
  id: Identifier,
  body: BlockStatement,
  params: Pattern[],
}

export type VariableDeclarator = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: 'VariableDeclarator',
  id: Pattern,
  init: ?Expression,
}

const a : any = null;

export const builders : {
  emptyStatement() : EmptyStatement,
  blockStatement(
    body: Statement[]
  ) : BlockStatement,
  expressionStatement(
    expression: Expression
  ) : ExpressionStatement,
  ifStatement(
    test: Expression,
    consequent: Statement,
    alternate?: Statement
  ) : IfStatement,
  breakStatement(
    label?: Identifier
  ) : BreakStatement,
  continueStatement(
    label?: Identifier
  ) : ContinueStatement,
  returnStatement(
    argument: ?Expression
  ) : ReturnStatement,
  throwStatement(
    argument: ?Expression
  ) : ThrowStatement,
  whileStatement(
    test: Expression,
    body: Statement
  ) : WhileStatement,
  forStatement(
    init: ?(VariableDeclaration | Expression),
    test: ?Expression,
    update: ?Expression,
    body: Statement
  ) : ForStatement,
  forInStatement(
    left: VariableDeclaration | Expression,
    right: Expression,
    body: Statement
  ) : ForInStatement,
  tryStatement(
    block: BlockStatement,
    handler: ?CatchClause,
    handlers: CatchClause[],
    finalizer?: BlockStatement
  ) : TryStatement,
  catchClause(
    param: Pattern,
    guard: ?Expression,
    body: BlockStatement
  ) : CatchClause,
  identifier(
    name: string
  ) : Identifier,
  literal(
    value: ?(string | boolean | number | RegExp),
    regex?: { pattern: string, flags: string }
  ) : Literal,
  thisExpression() : ThisExpression,
  arrayExpression(
    elements: Expression[]
  ) : ArrayExpression,
  objectExpreession(
    properties: Property[]
  ) : ObjectExpreession,
  property(
    kind: 'init' | 'get' | 'set',
    key: Literal | Identifier,
    value: Expression
  ) : Property,
  functionExpression(
    id: ?Identifier,
    params: Pattern[],
    body: BlockStatement
  ) : FunctionExpression,
  binaryExpression(
    operator: BinaryOperator,
    left: Expression,
    right: Expression
  ) : BinaryExpression,
  unaryExpression(
    operator: UnaryOperator,
    argument: Expression,
    prefix: boolean
  ) : UnaryExpression,
  assignmentExpression(
    operator: AssignmentOperator,
    left: Pattern,
    right: Expression
  ) : AssignmentExpression,
  updateExpression(
    operator: UpdateOperator,
    argument: Expression,
    prefix: boolean
  ) : UpdateExpression,
  logicalExpression(
    operator: LogicalOperator,
    left: Expression,
    right: Expression
  ) : LogicalExpression,
  conditionalExpression(
    test: Expression,
    consequent: Expression,
    alternate: Expression
  ) : ConditionalExpression,
  newExpression(
    callee: Expression,
    arguments: Expression[]
  ) : NewExpression,
  callExpression(
    callee: Expression,
    arguments: Expression[]
  ) : CallExpression,
  memberExpression(
    object: Expression,
    property: Identifier | Expression,
    computed: bool
  ) : MemberExpression,
  variableDeclaration(
    kind: 'var' | 'let' | 'const',
    declarations: VariableDeclarator[]
  ) : VariableDeclaration,
  functionDeclaration(
    id: Identifier,
    body: BlockStatement,
    params: Pattern[]
  ) : FunctionDeclaration,
  variableDeclarator(
    id: Pattern,
    init?: Expression
  ) : VariableDeclarator,
} = a;

=====================================output=====================================
/**
 * @flow
 */
export type Comment = {
  loc: ?SourceLocation,
  value: string,
  leading: boolean,
  trailing: boolean
};

export type SourceLocation = {
  start: SourcePosition,
  end: SourcePosition,
  source: ?string
};

export type SourcePosition = {
  line: number,
  column: number
};

export type File = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "File",
  program: Program
};

export type Program = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "Program",
  body: Statement[]
};

export type BinaryOperator =
  | "=="
  | "!="
  | "==="
  | "!=="
  | "<"
  | "<="
  | ">"
  | ">="
  | "<<"
  | ">>"
  | ">>>"
  | "+"
  | "-"
  | "*"
  | "/"
  | "%"
  | "&" // TODO Missing from the Parser API.
  | "|"
  | "^"
  | "in"
  | "instanceof"
  | "..";

export type UnaryOperator =
  | "-"
  | "+"
  | "!"
  | "~"
  | "typeof"
  | "void"
  | "delete";

export type AssignmentOperator =
  | "="
  | "+="
  | "-="
  | "*="
  | "/="
  | "%="
  | "<<="
  | ">>="
  | ">>>="
  | "|="
  | "^="
  | "&=";

export type UpdateOperator = "++" | "--";

export type LogicalOperator = "&&" | "||";

export type Node =
  | EmptyStatement
  | BlockStatement
  | ExpressionStatement
  | IfStatement
  | BreakStatement
  | ContinueStatement
  | ReturnStatement
  | ThrowStatement
  | WhileStatement
  | ForStatement
  | ForInStatement
  | TryStatement
  | CatchClause
  | Identifier
  | Literal
  | ThisExpression
  | ArrayExpression
  | ObjectExpreession
  | Property
  | FunctionExpression
  | BinaryExpression
  | UnaryExpression
  | AssignmentExpression
  | UpdateExpression
  | LogicalExpression
  | ConditionalExpression
  | NewExpression
  | CallExpression
  | MemberExpression
  | VariableDeclaration
  | FunctionDeclaration
  | VariableDeclarator;

export type Statement =
  | BlockStatement
  | EmptyStatement
  | ExpressionStatement
  | IfStatement
  | BreakStatement
  | ContinueStatement
  | ReturnStatement
  | ThrowStatement
  | WhileStatement
  | ForStatement
  | ForInStatement
  | TryStatement
  | Declaration;

export type EmptyStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "EmptyStatement"
};

export type BlockStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "BlockStatement",
  body: Statement[]
};

export type ExpressionStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "ExpressionStatement",
  expression: Expression
};

export type IfStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "IfStatement",
  test: Expression,
  consequent: Statement,
  alternate: ?Statement
};

export type BreakStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "BreakStatement",
  label: ?Identifier
};

export type ContinueStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "ContinueStatement",
  label: ?Identifier
};

export type ReturnStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "ReturnStatement",
  argument: ?Expression
};

export type ThrowStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "ThrowStatement",
  argument: ?Expression
};

export type WhileStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "WhileStatement",
  test: Expression,
  body: Statement
};

export type ForStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "ForStatement",
  init: ?(VariableDeclaration | Expression),
  test: ?Expression,
  update: ?Expression,
  body: Statement
};

export type ForInStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "ForInStatement",
  left: VariableDeclaration | Expression,
  right: Expression,
  body: Statement
};

export type TryStatement = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "TryStatement",
  block: BlockStatement,
  handler: ?CatchClause,
  handlers: CatchClause[],
  finalizer: ?BlockStatement
};

export type CatchClause = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "CatchClause",
  param: Pattern,
  guard: ?Expression,
  body: BlockStatement
};

export type Expression =
  | Identifier
  | ThisExpression
  | Literal
  | FunctionExpression
  | BinaryExpression
  | UnaryExpression
  | AssignmentExpression
  | UpdateExpression
  | LogicalExpression
  | ConditionalExpression
  | NewExpression
  | CallExpression
  | MemberExpression
  | ArrayExpression
  | ObjectExpreession;

export type Identifier = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "Identifier",
  name: string
};

export type Literal = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "Literal",
  value: ?(string | boolean | number | RegExp),
  regex: ?{ pattern: string, flags: string }
};

export type ThisExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "ThisExpression"
};

export type ArrayExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "ArrayExpression",
  elements: Expression[]
};

export type ObjectExpreession = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "ObjectExpression",
  properties: Property[]
};

export type Property = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "Property",
  kind: "init" | "get" | "set",
  key: Literal | Identifier,
  value: Expression
};

export type FunctionExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "FunctionExpression",
  id: ?Identifier,
  params: Pattern[],
  body: BlockStatement
};

export type BinaryExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "BinaryExpression",
  operator: BinaryOperator,
  left: Expression,
  right: Expression
};

export type UnaryExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "UnaryExpression",
  operator: UnaryOperator,
  argument: Expression,
  prefix: boolean
};

export type AssignmentExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "AssignmentExpression",
  operator: AssignmentOperator,
  left: Pattern,
  right: Expression
};

export type UpdateExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "UpdateExpression",
  operator: UpdateOperator,
  argument: Expression,
  prefix: boolean
};

export type LogicalExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "LogicalExpression",
  operator: LogicalOperator,
  left: Expression,
  right: Expression
};

export type ConditionalExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "ConditionalExpression",
  test: Expression,
  consequent: Expression,
  alternate: Expression
};

export type NewExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "NewExpression",
  callee: Expression,
  arguments: Expression[]
};

export type CallExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "CallExpression",
  callee: Expression,
  arguments: Expression[]
};

export type MemberExpression = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "MemberExpression",
  object: Expression,
  property: Identifier | Expression,
  computed: boolean
};
// ast-types exports all expressions as patterns.
// That seems not like it was intended.
export type Pattern = Identifier;

export type Declaration = VariableDeclaration | FunctionDeclaration;

export type VariableDeclaration = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "VariableDeclaration",
  kind: "var" | "let" | "const",
  declarations: VariableDeclarator[]
};

export type FunctionDeclaration = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "FunctionDeclaration",
  id: Identifier,
  body: BlockStatement,
  params: Pattern[]
};

export type VariableDeclarator = {
  source: ?string,
  start: SourcePosition,
  end: SourcePosition,
  comments: ?Array<Comment>,
  type: "VariableDeclarator",
  id: Pattern,
  init: ?Expression
};

const a: any = null;

export const builders: {
  emptyStatement(): EmptyStatement,
  blockStatement(body: Statement[]): BlockStatement,
  expressionStatement(expression: Expression): ExpressionStatement,
  ifStatement(
    test: Expression,
    consequent: Statement,
    alternate?: Statement
  ): IfStatement,
  breakStatement(label?: Identifier): BreakStatement,
  continueStatement(label?: Identifier): ContinueStatement,
  returnStatement(argument: ?Expression): ReturnStatement,
  throwStatement(argument: ?Expression): ThrowStatement,
  whileStatement(test: Expression, body: Statement): WhileStatement,
  forStatement(
    init: ?(VariableDeclaration | Expression),
    test: ?Expression,
    update: ?Expression,
    body: Statement
  ): ForStatement,
  forInStatement(
    left: VariableDeclaration | Expression,
    right: Expression,
    body: Statement
  ): ForInStatement,
  tryStatement(
    block: BlockStatement,
    handler: ?CatchClause,
    handlers: CatchClause[],
    finalizer?: BlockStatement
  ): TryStatement,
  catchClause(
    param: Pattern,
    guard: ?Expression,
    body: BlockStatement
  ): CatchClause,
  identifier(name: string): Identifier,
  literal(
    value: ?(string | boolean | number | RegExp),
    regex?: { pattern: string, flags: string }
  ): Literal,
  thisExpression(): ThisExpression,
  arrayExpression(elements: Expression[]): ArrayExpression,
  objectExpreession(properties: Property[]): ObjectExpreession,
  property(
    kind: "init" | "get" | "set",
    key: Literal | Identifier,
    value: Expression
  ): Property,
  functionExpression(
    id: ?Identifier,
    params: Pattern[],
    body: BlockStatement
  ): FunctionExpression,
  binaryExpression(
    operator: BinaryOperator,
    left: Expression,
    right: Expression
  ): BinaryExpression,
  unaryExpression(
    operator: UnaryOperator,
    argument: Expression,
    prefix: boolean
  ): UnaryExpression,
  assignmentExpression(
    operator: AssignmentOperator,
    left: Pattern,
    right: Expression
  ): AssignmentExpression,
  updateExpression(
    operator: UpdateOperator,
    argument: Expression,
    prefix: boolean
  ): UpdateExpression,
  logicalExpression(
    operator: LogicalOperator,
    left: Expression,
    right: Expression
  ): LogicalExpression,
  conditionalExpression(
    test: Expression,
    consequent: Expression,
    alternate: Expression
  ): ConditionalExpression,
  newExpression(callee: Expression, arguments: Expression[]): NewExpression,
  callExpression(callee: Expression, arguments: Expression[]): CallExpression,
  memberExpression(
    object: Expression,
    property: Identifier | Expression,
    computed: boolean
  ): MemberExpression,
  variableDeclaration(
    kind: "var" | "let" | "const",
    declarations: VariableDeclarator[]
  ): VariableDeclaration,
  functionDeclaration(
    id: Identifier,
    body: BlockStatement,
    params: Pattern[]
  ): FunctionDeclaration,
  variableDeclarator(id: Pattern, init?: Expression): VariableDeclarator
} = a;

================================================================================
`;
