Faster tokenizer lookahead (#13341)

* refactor: simplify token context structure

* add benchmark

* perf: return a sub-state on tokenizer lookahead

* Update packages/babel-parser/src/tokenizer/index.js

Co-authored-by: Brian Ng <bng412@gmail.com>

* Update packages/babel-parser/src/tokenizer/index.js

Co-authored-by: Brian Ng <bng412@gmail.com>

* remove irrelevant comment

* fix: guard curPosition with isLookahead

* add test cases

Co-authored-by: Brian Ng <bng412@gmail.com>
This commit is contained in:
Huáng Jùnliàng 2021-05-25 21:12:38 -04:00 committed by GitHub
parent b1f57e5fb5
commit acf2a10899
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 684 additions and 29 deletions

View File

@ -0,0 +1,22 @@
import Benchmark from "benchmark";
import baseline from "@babel-baseline/parser";
import current from "../../lib/index.js";
import { report } from "../util.mjs";
const suite = new Benchmark.Suite();
function createInput(length) {
return "type A = " + "| (x) => void".repeat(length);
}
function benchCases(name, implementation, options) {
for (const length of [256, 512, 1024, 2048]) {
const input = createInput(length);
suite.add(`${name} ${length} arrow function types`, () => {
implementation.parse(input, options);
});
}
}
benchCases("baseline", baseline, { plugins: ["flow"] });
benchCases("current", current, { plugins: ["flow"] });
suite.on("cycle", report).run();

View File

@ -9,7 +9,6 @@ import type Parser from "../../parser";
import { types as tt, type TokenType } from "../../tokenizer/types"; import { types as tt, type TokenType } from "../../tokenizer/types";
import * as N from "../../types"; import * as N from "../../types";
import type { Pos, Position } from "../../util/location"; import type { Pos, Position } from "../../util/location";
import type State from "../../tokenizer/state";
import { types as tc } from "../../tokenizer/context"; import { types as tc } from "../../tokenizer/context";
import * as charCodes from "charcodes"; import * as charCodes from "charcodes";
import { isIteratorStart, isKeyword } from "../../util/identifier"; import { isIteratorStart, isKeyword } from "../../util/identifier";
@ -154,7 +153,7 @@ function hasTypeImportKind(node: N.Node): boolean {
return node.importKind === "type" || node.importKind === "typeof"; return node.importKind === "type" || node.importKind === "typeof";
} }
function isMaybeDefaultImport(state: State): boolean { function isMaybeDefaultImport(state: { type: TokenType, value: any }): boolean {
return ( return (
(state.type === tt.name || !!state.type.keyword) && state.value !== "from" (state.type === tt.name || !!state.type.keyword) && state.value !== "from"
); );

View File

@ -15,6 +15,10 @@ import { isIdentifierChar, isIdentifierStart } from "../../util/identifier";
import type { Position } from "../../util/location"; import type { Position } from "../../util/location";
import { isNewLine } from "../../util/whitespace"; import { isNewLine } from "../../util/whitespace";
import { Errors, makeErrorTemplates, ErrorCodes } from "../../parser/error"; import { Errors, makeErrorTemplates, ErrorCodes } from "../../parser/error";
import type { LookaheadState } from "../../tokenizer/state";
import State from "../../tokenizer/state";
type JSXLookaheadState = LookaheadState & { inPropertyName: boolean };
const HEX_NUMBER = /^[\da-fA-F]+$/; const HEX_NUMBER = /^[\da-fA-F]+$/;
const DECIMAL_NUMBER = /^\d+$/; const DECIMAL_NUMBER = /^\d+$/;
@ -573,6 +577,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
} }
} }
createLookaheadState(state: State): JSXLookaheadState {
const lookaheadState = ((super.createLookaheadState(
state,
): any): JSXLookaheadState);
lookaheadState.inPropertyName = state.inPropertyName;
return lookaheadState;
}
getTokenFromCode(code: number): void { getTokenFromCode(code: number): void {
if (this.state.inPropertyName) return super.getTokenFromCode(code); if (this.state.inPropertyName) return super.getTokenFromCode(code);

View File

@ -7,22 +7,15 @@
import { types as tt } from "./types"; import { types as tt } from "./types";
export class TokContext { export class TokContext {
constructor( constructor(token: string, isExpr?: boolean, preserveSpace?: boolean) {
token: string,
isExpr?: boolean,
preserveSpace?: boolean,
override?: ?Function, // Takes a Tokenizer as a this-parameter, and returns void.
) {
this.token = token; this.token = token;
this.isExpr = !!isExpr; this.isExpr = !!isExpr;
this.preserveSpace = !!preserveSpace; this.preserveSpace = !!preserveSpace;
this.override = override;
} }
token: string; token: string;
isExpr: boolean; isExpr: boolean;
preserveSpace: boolean; preserveSpace: boolean;
override: ?Function;
} }
export const types: { export const types: {
@ -34,7 +27,7 @@ export const types: {
templateQuasi: new TokContext("${", false), templateQuasi: new TokContext("${", false),
parenStatement: new TokContext("(", false), parenStatement: new TokContext("(", false),
parenExpression: new TokContext("(", true), parenExpression: new TokContext("(", true),
template: new TokContext("`", true, true, p => p.readTmplToken()), template: new TokContext("`", true, true),
functionExpression: new TokContext("function", true), functionExpression: new TokContext("function", true),
functionStatement: new TokContext("function", false), functionStatement: new TokContext("function", false),
}; };

View File

@ -19,6 +19,7 @@ import {
skipWhiteSpace, skipWhiteSpace,
} from "../util/whitespace"; } from "../util/whitespace";
import State from "./state"; import State from "./state";
import type { LookaheadState } from "./state";
const VALID_REGEX_FLAGS = new Set(["g", "m", "s", "i", "y", "u"]); const VALID_REGEX_FLAGS = new Set(["g", "m", "s", "i", "y", "u"]);
@ -144,12 +145,10 @@ export default class Tokenizer extends ParserErrors {
// Move to the next token // Move to the next token
next(): void { next(): void {
if (!this.isLookahead) {
this.checkKeywordEscapes(); this.checkKeywordEscapes();
if (this.options.tokens) { if (this.options.tokens) {
this.pushToken(new Token(this.state)); this.pushToken(new Token(this.state));
} }
}
this.state.lastTokEnd = this.state.end; this.state.lastTokEnd = this.state.end;
this.state.lastTokStart = this.state.start; this.state.lastTokStart = this.state.start;
@ -175,14 +174,51 @@ export default class Tokenizer extends ParserErrors {
return this.state.type === type; return this.state.type === type;
} }
// TODO /**
* Create a LookaheadState from current parser state
*
* @param {State} state
* @returns {LookaheadState}
* @memberof Tokenizer
*/
createLookaheadState(state: State): LookaheadState {
return {
pos: state.pos,
value: null,
type: state.type,
start: state.start,
end: state.end,
lastTokEnd: state.end,
context: [this.curContext()],
exprAllowed: state.exprAllowed,
inType: state.inType,
};
}
lookahead(): State { /**
* lookahead peeks the next token, skipping changes to token context and
* comment stack. For performance it returns a limited LookaheadState
* instead of full parser state.
*
* The { column, line } Loc info is not included in lookahead since such usage
* is rare. Although it may return other location properties e.g. `curLine` and
* `lineStart`, these properties are not listed in the LookaheadState interface
* and thus the returned value is _NOT_ reliable.
*
* The tokenizer should make best efforts to avoid using any parser state
* other than those defined in LookaheadState
*
* @returns {LookaheadState}
* @memberof Tokenizer
*/
lookahead(): LookaheadState {
const old = this.state; const old = this.state;
this.state = old.clone(true); // For performance we use a simpified tokenizer state structure
// $FlowIgnore
this.state = this.createLookaheadState(old);
this.isLookahead = true; this.isLookahead = true;
this.next(); this.nextToken();
this.isLookahead = false; this.isLookahead = false;
const curr = this.state; const curr = this.state;
@ -247,17 +283,16 @@ export default class Tokenizer extends ParserErrors {
nextToken(): void { nextToken(): void {
const curContext = this.curContext(); const curContext = this.curContext();
if (!curContext?.preserveSpace) this.skipSpace(); if (!curContext.preserveSpace) this.skipSpace();
this.state.start = this.state.pos; this.state.start = this.state.pos;
this.state.startLoc = this.state.curPosition(); if (!this.isLookahead) this.state.startLoc = this.state.curPosition();
if (this.state.pos >= this.length) { if (this.state.pos >= this.length) {
this.finishToken(tt.eof); this.finishToken(tt.eof);
return; return;
} }
const override = curContext?.override; if (curContext === ct.template) {
if (override) { this.readTmplToken();
override(this);
} else { } else {
this.getTokenFromCode(this.codePointAtPos(this.state.pos)); this.getTokenFromCode(this.codePointAtPos(this.state.pos));
} }
@ -285,7 +320,8 @@ export default class Tokenizer extends ParserErrors {
} }
skipBlockComment(): void { skipBlockComment(): void {
const startLoc = this.state.curPosition(); let startLoc;
if (!this.isLookahead) startLoc = this.state.curPosition();
const start = this.state.pos; const start = this.state.pos;
const end = this.input.indexOf("*/", this.state.pos + 2); const end = this.input.indexOf("*/", this.state.pos + 2);
if (end === -1) throw this.raise(start, Errors.UnterminatedComment); if (end === -1) throw this.raise(start, Errors.UnterminatedComment);
@ -304,6 +340,7 @@ export default class Tokenizer extends ParserErrors {
// If we are doing a lookahead right now we need to advance the position (above code) // If we are doing a lookahead right now we need to advance the position (above code)
// but we do not want to push the comment to the state. // but we do not want to push the comment to the state.
if (this.isLookahead) return; if (this.isLookahead) return;
/*:: invariant(startLoc) */
this.pushComment( this.pushComment(
true, true,
@ -317,7 +354,8 @@ export default class Tokenizer extends ParserErrors {
skipLineComment(startSkip: number): void { skipLineComment(startSkip: number): void {
const start = this.state.pos; const start = this.state.pos;
const startLoc = this.state.curPosition(); let startLoc;
if (!this.isLookahead) startLoc = this.state.curPosition();
let ch = this.input.charCodeAt((this.state.pos += startSkip)); let ch = this.input.charCodeAt((this.state.pos += startSkip));
if (this.state.pos < this.length) { if (this.state.pos < this.length) {
while (!isNewLine(ch) && ++this.state.pos < this.length) { while (!isNewLine(ch) && ++this.state.pos < this.length) {
@ -328,6 +366,7 @@ export default class Tokenizer extends ParserErrors {
// If we are doing a lookahead right now we need to advance the position (above code) // If we are doing a lookahead right now we need to advance the position (above code)
// but we do not want to push the comment to the state. // but we do not want to push the comment to the state.
if (this.isLookahead) return; if (this.isLookahead) return;
/*:: invariant(startLoc) */
this.pushComment( this.pushComment(
false, false,
@ -398,12 +437,14 @@ export default class Tokenizer extends ParserErrors {
finishToken(type: TokenType, val: any): void { finishToken(type: TokenType, val: any): void {
this.state.end = this.state.pos; this.state.end = this.state.pos;
this.state.endLoc = this.state.curPosition();
const prevType = this.state.type; const prevType = this.state.type;
this.state.type = type; this.state.type = type;
this.state.value = val; this.state.value = val;
if (!this.isLookahead) this.updateContext(prevType); if (!this.isLookahead) {
this.state.endLoc = this.state.curPosition();
this.updateContext(prevType);
}
} }
// ### Token reading // ### Token reading

View File

@ -178,3 +178,14 @@ export default class State {
return state; return state;
} }
} }
export type LookaheadState = {
pos: number,
value: any,
type: TokenType,
start: number,
end: number,
/* Used only in readSlashToken */
exprAllowed: boolean,
inType: boolean,
};

View File

@ -0,0 +1,2 @@
/*1*/ export /*2*/ { /*3*/ A /*4*/, /*5*/ B /*6*/ as /*7*/ C /*8*/ } /*9*/ from /*10*/ "foo";
/*1*/ export /*2*/ * /*3*/ from /*4*/ "foo"

View File

@ -0,0 +1,242 @@
{
"type": "File",
"start":0,"end":137,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":43}},
"program": {
"type": "Program",
"start":0,"end":137,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":43}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ExportNamedDeclaration",
"start":6,"end":93,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":93}},
"leadingComments": [
{
"type": "CommentBlock",
"value": "1",
"start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}}
}
],
"trailingComments": [
{
"type": "CommentBlock",
"value": "1",
"start":94,"end":99,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":5}}
}
],
"exportKind": "value",
"specifiers": [
{
"type": "ExportSpecifier",
"start":27,"end":28,"loc":{"start":{"line":1,"column":27},"end":{"line":1,"column":28}},
"leadingComments": [
{
"type": "CommentBlock",
"value": "2",
"start":13,"end":18,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":18}}
},
{
"type": "CommentBlock",
"value": "3",
"start":21,"end":26,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":26}}
}
],
"trailingComments": [
{
"type": "CommentBlock",
"value": "4",
"start":29,"end":34,"loc":{"start":{"line":1,"column":29},"end":{"line":1,"column":34}}
},
{
"type": "CommentBlock",
"value": "5",
"start":36,"end":41,"loc":{"start":{"line":1,"column":36},"end":{"line":1,"column":41}}
}
],
"local": {
"type": "Identifier",
"start":27,"end":28,"loc":{"start":{"line":1,"column":27},"end":{"line":1,"column":28},"identifierName":"A"},
"name": "A"
},
"exported": {
"type": "Identifier",
"start":27,"end":28,"loc":{"start":{"line":1,"column":27},"end":{"line":1,"column":28},"identifierName":"A"},
"name": "A"
}
},
{
"type": "ExportSpecifier",
"start":42,"end":60,"loc":{"start":{"line":1,"column":42},"end":{"line":1,"column":60}},
"trailingComments": [
{
"type": "CommentBlock",
"value": "8",
"start":61,"end":66,"loc":{"start":{"line":1,"column":61},"end":{"line":1,"column":66}}
},
{
"type": "CommentBlock",
"value": "9",
"start":69,"end":74,"loc":{"start":{"line":1,"column":69},"end":{"line":1,"column":74}}
},
{
"type": "CommentBlock",
"value": "10",
"start":80,"end":86,"loc":{"start":{"line":1,"column":80},"end":{"line":1,"column":86}}
}
],
"local": {
"type": "Identifier",
"start":42,"end":43,"loc":{"start":{"line":1,"column":42},"end":{"line":1,"column":43},"identifierName":"B"},
"trailingComments": [
{
"type": "CommentBlock",
"value": "6",
"start":44,"end":49,"loc":{"start":{"line":1,"column":44},"end":{"line":1,"column":49}}
}
],
"name": "B"
},
"exported": {
"type": "Identifier",
"start":59,"end":60,"loc":{"start":{"line":1,"column":59},"end":{"line":1,"column":60},"identifierName":"C"},
"leadingComments": [
{
"type": "CommentBlock",
"value": "6",
"start":44,"end":49,"loc":{"start":{"line":1,"column":44},"end":{"line":1,"column":49}}
},
{
"type": "CommentBlock",
"value": "7",
"start":53,"end":58,"loc":{"start":{"line":1,"column":53},"end":{"line":1,"column":58}}
}
],
"name": "C"
}
}
],
"source": {
"type": "StringLiteral",
"start":87,"end":92,"loc":{"start":{"line":1,"column":87},"end":{"line":1,"column":92}},
"extra": {
"rawValue": "foo",
"raw": "\"foo\""
},
"value": "foo"
},
"declaration": null
},
{
"type": "ExportAllDeclaration",
"start":100,"end":137,"loc":{"start":{"line":2,"column":6},"end":{"line":2,"column":43}},
"leadingComments": [
{
"type": "CommentBlock",
"value": "1",
"start":94,"end":99,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":5}}
}
],
"exportKind": "value",
"source": {
"type": "StringLiteral",
"start":132,"end":137,"loc":{"start":{"line":2,"column":38},"end":{"line":2,"column":43}},
"leadingComments": [
{
"type": "CommentBlock",
"value": "2",
"start":107,"end":112,"loc":{"start":{"line":2,"column":13},"end":{"line":2,"column":18}}
},
{
"type": "CommentBlock",
"value": "3",
"start":115,"end":120,"loc":{"start":{"line":2,"column":21},"end":{"line":2,"column":26}}
},
{
"type": "CommentBlock",
"value": "4",
"start":126,"end":131,"loc":{"start":{"line":2,"column":32},"end":{"line":2,"column":37}}
}
],
"extra": {
"rawValue": "foo",
"raw": "\"foo\""
},
"value": "foo"
}
}
],
"directives": []
},
"comments": [
{
"type": "CommentBlock",
"value": "1",
"start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}}
},
{
"type": "CommentBlock",
"value": "2",
"start":13,"end":18,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":18}}
},
{
"type": "CommentBlock",
"value": "3",
"start":21,"end":26,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":26}}
},
{
"type": "CommentBlock",
"value": "4",
"start":29,"end":34,"loc":{"start":{"line":1,"column":29},"end":{"line":1,"column":34}}
},
{
"type": "CommentBlock",
"value": "5",
"start":36,"end":41,"loc":{"start":{"line":1,"column":36},"end":{"line":1,"column":41}}
},
{
"type": "CommentBlock",
"value": "6",
"start":44,"end":49,"loc":{"start":{"line":1,"column":44},"end":{"line":1,"column":49}}
},
{
"type": "CommentBlock",
"value": "7",
"start":53,"end":58,"loc":{"start":{"line":1,"column":53},"end":{"line":1,"column":58}}
},
{
"type": "CommentBlock",
"value": "8",
"start":61,"end":66,"loc":{"start":{"line":1,"column":61},"end":{"line":1,"column":66}}
},
{
"type": "CommentBlock",
"value": "9",
"start":69,"end":74,"loc":{"start":{"line":1,"column":69},"end":{"line":1,"column":74}}
},
{
"type": "CommentBlock",
"value": "10",
"start":80,"end":86,"loc":{"start":{"line":1,"column":80},"end":{"line":1,"column":86}}
},
{
"type": "CommentBlock",
"value": "1",
"start":94,"end":99,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":5}}
},
{
"type": "CommentBlock",
"value": "2",
"start":107,"end":112,"loc":{"start":{"line":2,"column":13},"end":{"line":2,"column":18}}
},
{
"type": "CommentBlock",
"value": "3",
"start":115,"end":120,"loc":{"start":{"line":2,"column":21},"end":{"line":2,"column":26}}
},
{
"type": "CommentBlock",
"value": "4",
"start":126,"end":131,"loc":{"start":{"line":2,"column":32},"end":{"line":2,"column":37}}
}
]
}

View File

@ -0,0 +1,2 @@
/*1*/ import /*2*/ D /*3*/, /*4*/ { /*5*/ A /*6*/, /*7*/ B /*8*/ as /*9*/ C /*10*/ } /*11*/ from /*12*/ "foo";
/*1*/ import /*2*/ * /*3*/ as /*4*/ foo /*5*/ from /*6*/ "foo";

View File

@ -0,0 +1,331 @@
{
"type": "File",
"start":0,"end":174,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":63}},
"program": {
"type": "Program",
"start":0,"end":174,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":63}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ImportDeclaration",
"start":6,"end":110,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":110}},
"leadingComments": [
{
"type": "CommentBlock",
"value": "1",
"start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}}
}
],
"trailingComments": [
{
"type": "CommentBlock",
"value": "1",
"start":111,"end":116,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":5}}
}
],
"importKind": "value",
"specifiers": [
{
"type": "ImportDefaultSpecifier",
"start":19,"end":20,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":20}},
"leadingComments": [
{
"type": "CommentBlock",
"value": "2",
"start":13,"end":18,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":18}}
}
],
"trailingComments": [
{
"type": "CommentBlock",
"value": "3",
"start":21,"end":26,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":26}}
}
],
"local": {
"type": "Identifier",
"start":19,"end":20,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":20},"identifierName":"D"},
"name": "D"
}
},
{
"type": "ImportSpecifier",
"start":42,"end":43,"loc":{"start":{"line":1,"column":42},"end":{"line":1,"column":43}},
"leadingComments": [
{
"type": "CommentBlock",
"value": "1",
"start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}}
},
{
"type": "CommentBlock",
"value": "2",
"start":13,"end":18,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":18}}
},
{
"type": "CommentBlock",
"value": "3",
"start":21,"end":26,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":26}}
},
{
"type": "CommentBlock",
"value": "4",
"start":28,"end":33,"loc":{"start":{"line":1,"column":28},"end":{"line":1,"column":33}}
},
{
"type": "CommentBlock",
"value": "5",
"start":36,"end":41,"loc":{"start":{"line":1,"column":36},"end":{"line":1,"column":41}}
}
],
"trailingComments": [
{
"type": "CommentBlock",
"value": "6",
"start":44,"end":49,"loc":{"start":{"line":1,"column":44},"end":{"line":1,"column":49}}
},
{
"type": "CommentBlock",
"value": "7",
"start":51,"end":56,"loc":{"start":{"line":1,"column":51},"end":{"line":1,"column":56}}
}
],
"imported": {
"type": "Identifier",
"start":42,"end":43,"loc":{"start":{"line":1,"column":42},"end":{"line":1,"column":43},"identifierName":"A"},
"name": "A"
},
"local": {
"type": "Identifier",
"start":42,"end":43,"loc":{"start":{"line":1,"column":42},"end":{"line":1,"column":43},"identifierName":"A"},
"name": "A"
}
},
{
"type": "ImportSpecifier",
"start":57,"end":75,"loc":{"start":{"line":1,"column":57},"end":{"line":1,"column":75}},
"trailingComments": [
{
"type": "CommentBlock",
"value": "10",
"start":76,"end":82,"loc":{"start":{"line":1,"column":76},"end":{"line":1,"column":82}}
},
{
"type": "CommentBlock",
"value": "11",
"start":85,"end":91,"loc":{"start":{"line":1,"column":85},"end":{"line":1,"column":91}}
},
{
"type": "CommentBlock",
"value": "12",
"start":97,"end":103,"loc":{"start":{"line":1,"column":97},"end":{"line":1,"column":103}}
}
],
"imported": {
"type": "Identifier",
"start":57,"end":58,"loc":{"start":{"line":1,"column":57},"end":{"line":1,"column":58},"identifierName":"B"},
"trailingComments": [
{
"type": "CommentBlock",
"value": "8",
"start":59,"end":64,"loc":{"start":{"line":1,"column":59},"end":{"line":1,"column":64}}
}
],
"name": "B"
},
"local": {
"type": "Identifier",
"start":74,"end":75,"loc":{"start":{"line":1,"column":74},"end":{"line":1,"column":75},"identifierName":"C"},
"leadingComments": [
{
"type": "CommentBlock",
"value": "8",
"start":59,"end":64,"loc":{"start":{"line":1,"column":59},"end":{"line":1,"column":64}}
},
{
"type": "CommentBlock",
"value": "9",
"start":68,"end":73,"loc":{"start":{"line":1,"column":68},"end":{"line":1,"column":73}}
}
],
"name": "C"
}
}
],
"source": {
"type": "StringLiteral",
"start":104,"end":109,"loc":{"start":{"line":1,"column":104},"end":{"line":1,"column":109}},
"extra": {
"rawValue": "foo",
"raw": "\"foo\""
},
"value": "foo"
}
},
{
"type": "ImportDeclaration",
"start":117,"end":174,"loc":{"start":{"line":2,"column":6},"end":{"line":2,"column":63}},
"leadingComments": [
{
"type": "CommentBlock",
"value": "1",
"start":111,"end":116,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":5}}
}
],
"importKind": "value",
"specifiers": [
{
"type": "ImportNamespaceSpecifier",
"start":130,"end":150,"loc":{"start":{"line":2,"column":19},"end":{"line":2,"column":39}},
"leadingComments": [
{
"type": "CommentBlock",
"value": "2",
"start":124,"end":129,"loc":{"start":{"line":2,"column":13},"end":{"line":2,"column":18}}
}
],
"trailingComments": [
{
"type": "CommentBlock",
"value": "5",
"start":151,"end":156,"loc":{"start":{"line":2,"column":40},"end":{"line":2,"column":45}}
}
],
"local": {
"type": "Identifier",
"start":147,"end":150,"loc":{"start":{"line":2,"column":36},"end":{"line":2,"column":39},"identifierName":"foo"},
"leadingComments": [
{
"type": "CommentBlock",
"value": "3",
"start":132,"end":137,"loc":{"start":{"line":2,"column":21},"end":{"line":2,"column":26}}
},
{
"type": "CommentBlock",
"value": "4",
"start":141,"end":146,"loc":{"start":{"line":2,"column":30},"end":{"line":2,"column":35}}
}
],
"name": "foo"
}
}
],
"source": {
"type": "StringLiteral",
"start":168,"end":173,"loc":{"start":{"line":2,"column":57},"end":{"line":2,"column":62}},
"leadingComments": [
{
"type": "CommentBlock",
"value": "5",
"start":151,"end":156,"loc":{"start":{"line":2,"column":40},"end":{"line":2,"column":45}}
},
{
"type": "CommentBlock",
"value": "6",
"start":162,"end":167,"loc":{"start":{"line":2,"column":51},"end":{"line":2,"column":56}}
}
],
"extra": {
"rawValue": "foo",
"raw": "\"foo\""
},
"value": "foo"
}
}
],
"directives": []
},
"comments": [
{
"type": "CommentBlock",
"value": "1",
"start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}}
},
{
"type": "CommentBlock",
"value": "2",
"start":13,"end":18,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":18}}
},
{
"type": "CommentBlock",
"value": "3",
"start":21,"end":26,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":26}}
},
{
"type": "CommentBlock",
"value": "4",
"start":28,"end":33,"loc":{"start":{"line":1,"column":28},"end":{"line":1,"column":33}}
},
{
"type": "CommentBlock",
"value": "5",
"start":36,"end":41,"loc":{"start":{"line":1,"column":36},"end":{"line":1,"column":41}}
},
{
"type": "CommentBlock",
"value": "6",
"start":44,"end":49,"loc":{"start":{"line":1,"column":44},"end":{"line":1,"column":49}}
},
{
"type": "CommentBlock",
"value": "7",
"start":51,"end":56,"loc":{"start":{"line":1,"column":51},"end":{"line":1,"column":56}}
},
{
"type": "CommentBlock",
"value": "8",
"start":59,"end":64,"loc":{"start":{"line":1,"column":59},"end":{"line":1,"column":64}}
},
{
"type": "CommentBlock",
"value": "9",
"start":68,"end":73,"loc":{"start":{"line":1,"column":68},"end":{"line":1,"column":73}}
},
{
"type": "CommentBlock",
"value": "10",
"start":76,"end":82,"loc":{"start":{"line":1,"column":76},"end":{"line":1,"column":82}}
},
{
"type": "CommentBlock",
"value": "11",
"start":85,"end":91,"loc":{"start":{"line":1,"column":85},"end":{"line":1,"column":91}}
},
{
"type": "CommentBlock",
"value": "12",
"start":97,"end":103,"loc":{"start":{"line":1,"column":97},"end":{"line":1,"column":103}}
},
{
"type": "CommentBlock",
"value": "1",
"start":111,"end":116,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":5}}
},
{
"type": "CommentBlock",
"value": "2",
"start":124,"end":129,"loc":{"start":{"line":2,"column":13},"end":{"line":2,"column":18}}
},
{
"type": "CommentBlock",
"value": "3",
"start":132,"end":137,"loc":{"start":{"line":2,"column":21},"end":{"line":2,"column":26}}
},
{
"type": "CommentBlock",
"value": "4",
"start":141,"end":146,"loc":{"start":{"line":2,"column":30},"end":{"line":2,"column":35}}
},
{
"type": "CommentBlock",
"value": "5",
"start":151,"end":156,"loc":{"start":{"line":2,"column":40},"end":{"line":2,"column":45}}
},
{
"type": "CommentBlock",
"value": "6",
"start":162,"end":167,"loc":{"start":{"line":2,"column":51},"end":{"line":2,"column":56}}
}
]
}