├── .npmignore ├── .gitignore ├── .editorconfig ├── test ├── index.js ├── print │ ├── cases │ │ ├── literals.js │ │ ├── classes.js │ │ ├── expressions.js │ │ ├── objects.js │ │ ├── functions.js │ │ ├── modules.js │ │ └── statements.js │ └── index.js ├── parse │ ├── statements │ │ ├── with.js │ │ ├── if.js │ │ ├── try-catch.js │ │ ├── let.js │ │ ├── switch.js │ │ ├── async-for-of.js │ │ ├── for-semi.js │ │ ├── labelled.js │ │ └── for-in-of.js │ ├── index.js │ ├── expressions │ │ ├── unary.js │ │ ├── primary.js │ │ ├── array.js │ │ ├── regular-expressions.js │ │ ├── binary.js │ │ ├── assignment.js │ │ ├── strings.js │ │ ├── exponentiation.js │ │ ├── spread.js │ │ ├── numbers.js │ │ ├── templates.js │ │ ├── object-computed-names.js │ │ └── object-rest-spread.js │ ├── functions │ │ ├── defaults.js │ │ ├── methods.js │ │ ├── classic.js │ │ ├── rest.js │ │ ├── trailing-comma.js │ │ ├── async-generators.js │ │ ├── get-set.js │ │ ├── arrows.js │ │ └── generators.js │ ├── classes │ │ └── class-fields.js │ ├── unicode │ │ └── unicode.js │ ├── destructuring │ │ ├── array-binding.js │ │ └── object-assignment.js │ └── modules │ │ ├── import.js │ │ └── export.js ├── annotations │ ├── index.js │ └── cases │ │ └── class.js ├── scope │ ├── cases │ │ ├── with.js │ │ ├── modules.js │ │ ├── block.js │ │ ├── catch.js │ │ ├── var-shadowing.js │ │ └── params.js │ └── index.js └── runner.js ├── util ├── repl.js ├── repl-init.js └── generate-unicode.js ├── package.json ├── src ├── index.js ├── LineMap.js ├── Unicode.js ├── Transform.js ├── Validate.js └── ScopeResolver.js ├── README.md ├── LICENSE └── .eslintrc.js /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | _* 3 | node_modules 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | _* 3 | node_modules 4 | dist/ 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = false 6 | indent_style = space 7 | indent_size = 2 8 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | require('./parse/index.js'); 2 | require('./scope/index.js'); 3 | require('./print/index.js'); 4 | require('./annotations/index.js'); 5 | -------------------------------------------------------------------------------- /util/repl.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const childProcess = require('child_process'); 3 | 4 | childProcess.spawn('node', ['-r', 'annotated', '-r', path.resolve(__dirname, './repl-init')], { 5 | stdio: 'inherit', 6 | env: process.env, 7 | }); 8 | -------------------------------------------------------------------------------- /test/print/cases/literals.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** 'hello\nworld'; **/ 4 | 'newlines are escaped': `'hello\\nworld';`, 5 | 6 | /** /foobar/; **/ 7 | 'regex - basic': '/foobar/;', 8 | 9 | /** /foo\/bar/; **/ 10 | 'regex - escaped forward slash': '/foo\\/bar/;', 11 | 12 | /** /foo[/]bar/; **/ 13 | 'regex - not escaped forward slash': '/foo[\\/]bar/;', 14 | 15 | }) 16 | -------------------------------------------------------------------------------- /test/parse/statements/with.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** with (x); **/ 4 | "with statement": { 5 | type: "Script", 6 | statements: [ 7 | { type: "WithStatement", 8 | object: 9 | { type: "Identifier", 10 | value: "x" 11 | }, 12 | body: 13 | { type: "EmptyStatement" 14 | } 15 | }] 16 | }, 17 | 18 | }) 19 | -------------------------------------------------------------------------------- /test/print/cases/classes.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** class C {} **/ 4 | 'empty class': `class C {}`, 5 | 6 | /** class C { x = 1; y = 2; m() {} n() {} static a = 1; static b = 2; static c() {} } **/ 7 | 'complex class': 8 | `class C { 9 | x = 1; 10 | y = 2; 11 | 12 | m() {} 13 | 14 | n() {} 15 | 16 | static a = 1; 17 | static b = 2; 18 | 19 | static c() {} 20 | }` 21 | 22 | }) 23 | -------------------------------------------------------------------------------- /test/annotations/index.js: -------------------------------------------------------------------------------- 1 | const { parse } = require('../../src'); 2 | const { runTests, objectLike } = require('../runner.js'); 3 | 4 | function process(source, options) { 5 | return parse(source, options).annotations; 6 | } 7 | 8 | function compare(a, b) { 9 | return objectLike(a, b, ['message']); 10 | } 11 | 12 | runTests({ 13 | dir: __dirname, 14 | process, 15 | compare, 16 | }); 17 | -------------------------------------------------------------------------------- /test/parse/statements/if.js: -------------------------------------------------------------------------------- 1 | [ 2 | 3 | /** if (x) { } **/ 4 | { type: "Script", 5 | statements: [ 6 | 7 | { type: "IfStatement", 8 | 9 | test: 10 | { type: "Identifier", 11 | value: "x" 12 | }, 13 | 14 | consequent: 15 | { type: "Block", 16 | statements: [] 17 | }, 18 | 19 | alternate: null 20 | }] 21 | }, 22 | 23 | ] 24 | -------------------------------------------------------------------------------- /test/print/cases/expressions.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** x ? y : z **/ 4 | 'conditional expression': `x ? y : z;`, 5 | 6 | /** a.b[c] **/ 7 | 'member expression': `a.b[c];`, 8 | 9 | /** new a(1) **/ 10 | 'new expression': `new a(1);`, 11 | 12 | /** new a **/ 13 | 'new expression without parens': `new a;`, 14 | 15 | /** delete x.y **/ 16 | 'keyword unary expression': `delete x.y;`, 17 | 18 | /** !x **/ 19 | 'operator char unary expression': `!x;`, 20 | 21 | }) 22 | -------------------------------------------------------------------------------- /test/print/index.js: -------------------------------------------------------------------------------- 1 | const { parse, print } = require('../../src'); 2 | const { runTests } = require('../runner.js'); 3 | 4 | function process(source, options) { 5 | return print(parse(source, options).ast).output; 6 | } 7 | 8 | function normalize(v) { 9 | return v.replace(/\n[ ]+\n/g, '\n\n'); 10 | } 11 | 12 | function compare(a, b) { 13 | return normalize(a) === normalize(b); 14 | } 15 | 16 | runTests({ 17 | dir: __dirname, 18 | process, 19 | compare, 20 | }); 21 | -------------------------------------------------------------------------------- /test/scope/cases/with.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** with (x); **/ 4 | 'with creates a scope': 5 | { type: 'var', 6 | names: {}, 7 | free: [ { type: 'Identifier', value: 'x', context: 'variable' } ], 8 | strict: false, 9 | children: 10 | [ { type: 'block', 11 | names: {}, 12 | free: [], 13 | strict: false, 14 | children: 15 | [ { type: 'with', 16 | names: {}, 17 | free: [], 18 | strict: false, 19 | children: [] } ] } ] }, 20 | 21 | }) 22 | -------------------------------------------------------------------------------- /test/parse/index.js: -------------------------------------------------------------------------------- 1 | const { parse } = require('../../src'); 2 | const { runTests, objectLike } = require('../runner.js'); 3 | 4 | const SKIP_KEYS = [ 5 | 'start', 6 | 'end', 7 | 'message', 8 | 'context', 9 | 'error', 10 | 'suffix', 11 | ]; 12 | 13 | // Returns true if the specified AST is 'like' another AST 14 | function astLike(a, b) { 15 | return objectLike(a, b, SKIP_KEYS); 16 | } 17 | 18 | runTests({ 19 | dir: __dirname, 20 | process: (input, options) => parse(input, options).ast, 21 | compare: astLike 22 | }); 23 | -------------------------------------------------------------------------------- /test/print/cases/objects.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** ({ x: 1, y }) **/ 4 | 'object literals': 5 | `({ 6 | x: 1, 7 | y 8 | });`, 9 | 10 | /** ({ x() { return 1; } }) **/ 11 | 'object methods': 12 | `({ 13 | x() { 14 | return 1; 15 | } 16 | });`, 17 | 18 | /** ({ get x() {} }) **/ 19 | 'getters': 20 | `({ 21 | get x() {} 22 | });`, 23 | 24 | /** ({}) **/ 25 | 'empty objects': '({});', 26 | 27 | /** ({ *g() {} }) **/ 28 | 'generator methods': 29 | `({ 30 | *g() {} 31 | });`, 32 | 33 | /** ({ x, y, z }) **/ 34 | 'concise objects': `({ x, y, z });`, 35 | 36 | }) 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esparse", 3 | "version": "0.6.11", 4 | "description": "An ECMAScript Parser", 5 | "homepage": "https://github.com/zenparsing/esparse", 6 | "main": "dist/index.js", 7 | "module": "src/index.js", 8 | "devDependencies": { 9 | "annotated": "^0.3.9", 10 | "eslint": "^4.19.1" 11 | }, 12 | "scripts": { 13 | "lint": "eslint src/*", 14 | "test": "node -r annotated test", 15 | "build": "git clean -dfX ./dist && annotated src -o dist -m", 16 | "prepublishOnly": "npm run lint && npm test && npm run build" 17 | }, 18 | "dependencies": {} 19 | } 20 | -------------------------------------------------------------------------------- /test/parse/statements/try-catch.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** try {} catch {} **/ 4 | "catch binding is optional": 5 | { 6 | type: 'Script', 7 | start: 0, 8 | end: 15, 9 | statements: 10 | [ { 11 | type: 'TryStatement', 12 | start: 0, 13 | end: 15, 14 | block: { type: 'Block', start: 4, end: 6, statements: [] }, 15 | handler: 16 | { 17 | type: 'CatchClause', 18 | start: 7, 19 | end: 15, 20 | param: null, 21 | body: { type: 'Block', start: 13, end: 15, statements: [] } }, 22 | finalizer: null } ] } 23 | 24 | }) 25 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { Parser } from './Parser.js'; 2 | import { Printer } from './Printer.js'; 3 | import { ScopeResolver } from './ScopeResolver.js'; 4 | import * as AST from './AST.js'; 5 | 6 | export { AST, parse, print, resolveScopes }; 7 | 8 | function print(ast, options) { 9 | return new Printer().print(ast, options); 10 | } 11 | 12 | function parse(input, options = {}) { 13 | let parser = new Parser(input, options); 14 | let result = options.module ? parser.parseModule() : parser.parseScript(); 15 | return result; 16 | } 17 | 18 | function resolveScopes(ast, options) { 19 | return new ScopeResolver().resolve(ast, options); 20 | } 21 | -------------------------------------------------------------------------------- /test/print/cases/functions.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** function f(a, b = 1) { a++ } **/ 4 | 'function with default arguments': 5 | `function f(a, b = 1) { 6 | a++; 7 | }`, 8 | 9 | /** function f(...[a]) {} **/ 10 | 'function with pattern rest element': 11 | `function f(...[a]) {}`, 12 | 13 | /** function* g() { yield 1; yield * []; } **/ 14 | 'generator functions': 15 | `function* g() { 16 | yield 1; 17 | yield * []; 18 | }`, 19 | 20 | /** function f({ x, y }) {} **/ 21 | 'destructuring params': 22 | `function f({ x, y }) {}`, 23 | 24 | /** var x; function f() {} **/ 25 | 'newline before function': 26 | `var x; 27 | 28 | function f() {}`, 29 | 30 | }) 31 | -------------------------------------------------------------------------------- /test/scope/cases/modules.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /*** import { x } from 'y' ***/ 4 | 'imported names': { 5 | type: 'var', 6 | names: {}, 7 | free: [], 8 | strict: true, 9 | children: 10 | [ { 11 | type: 'block', 12 | names: 13 | { x: 14 | { declarations: 15 | [ { 16 | type: 'Identifier', 17 | start: 9, 18 | end: 10, 19 | value: 'x', 20 | context: 'declaration' } ], 21 | references: [], 22 | const: true } }, 23 | free: [], 24 | strict: true, 25 | children: [] } ] }, 26 | 27 | }) 28 | -------------------------------------------------------------------------------- /test/parse/expressions/unary.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** delete x **/ 4 | 'unqualified delete in sloppy mode': 5 | { type: 'Script', 6 | start: 0, 7 | end: 8, 8 | statements: 9 | [ { type: 'ExpressionStatement', 10 | start: 0, 11 | end: 8, 12 | expression: 13 | { type: 'UnaryExpression', 14 | start: 0, 15 | end: 8, 16 | operator: 'delete', 17 | expression: 18 | { type: 'Identifier', 19 | start: 7, 20 | end: 8, 21 | value: 'x', 22 | context: 'variable' } } } ] }, 23 | 24 | 25 | /** "use strict"; delete x **/ 26 | 'unqualified delete cannot appear in strict mode': {}, 27 | 28 | /** "use strict"; delete (((x))) **/ 29 | 'unqualified delete within parens cannot appear in strict mode': {}, 30 | 31 | 32 | }) 33 | -------------------------------------------------------------------------------- /test/scope/cases/block.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** class C {} **/ 4 | 'class declarations are always block-scoped': 5 | { type: 'var', 6 | names: {}, 7 | free: [], 8 | strict: false, 9 | children: 10 | [ { type: 'block', 11 | names: 12 | { C: 13 | { declarations: 14 | [ { type: 'Identifier', 15 | start: 6, 16 | end: 7, 17 | value: 'C', 18 | context: 'declaration' } ], 19 | references: [], 20 | const: false } }, 21 | free: [], 22 | strict: false, 23 | children: 24 | [ { type: 'class', 25 | names: {}, 26 | free: [], 27 | strict: true, 28 | children: [] } ] } ] }, 29 | 30 | /** class C {} class C {} **/ 31 | 'duplicate class declarations not allowed': {}, 32 | 33 | }) 34 | -------------------------------------------------------------------------------- /src/LineMap.js: -------------------------------------------------------------------------------- 1 | function binarySearch(array, val) { 2 | let right = array.length - 1; 3 | let left = 0; 4 | 5 | while (left <= right) { 6 | let mid = (left + right) >> 1; 7 | let test = array[mid]; 8 | 9 | if (val === test) 10 | return mid; 11 | 12 | if (val < test) right = mid - 1; 13 | else left = mid + 1; 14 | } 15 | 16 | return right; // Lower bound 17 | } 18 | 19 | export class LineMap { 20 | 21 | constructor() { 22 | this.lines = [0]; 23 | this.lastLineBreak = -1; 24 | } 25 | 26 | addBreak(offset) { 27 | if (offset > this.lastLineBreak) 28 | this.lines.push(this.lastLineBreak = offset); 29 | } 30 | 31 | locate(offset) { 32 | let line = binarySearch(this.lines, offset); 33 | let lineOffset = this.lines[line]; 34 | 35 | return { 36 | line, 37 | column: offset - lineOffset, 38 | lineOffset, 39 | }; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /test/print/cases/modules.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /*** export { x }; ***/ 4 | 'exports names': 'export { x };', 5 | 6 | /*** export x from 'foo'; ***/ 7 | 'exports default from': `export x from 'foo';`, 8 | 9 | /*** export default x; ***/ 10 | 'exports default expression': `export default x;`, 11 | 12 | /*** export * as m from 'foo'; ***/ 13 | 'export namespace as': `export * as m from 'foo';`, 14 | 15 | /*** export * from 'foo'; ***/ 16 | 'export namespace': `export * from 'foo';`, 17 | 18 | /*** var x; export function f() {} var y; ***/ 19 | 'export function spacing': 20 | `var x; 21 | 22 | export function f() {} 23 | 24 | var y;`, 25 | 26 | /*** var x; export class C {} var y; ***/ 27 | 'export class spacing': 28 | `var x; 29 | 30 | export class C {} 31 | 32 | var y;`, 33 | 34 | /*** var x; export default class C {} var y; ***/ 35 | 'export default spacing': 36 | `var x; 37 | 38 | export default class C {} 39 | 40 | var y;`, 41 | 42 | }) 43 | -------------------------------------------------------------------------------- /util/repl-init.js: -------------------------------------------------------------------------------- 1 | const esparse = require('../src'); 2 | const util = require('util'); 3 | 4 | const HELP = ` 5 | == Global Variables == 6 | 7 | ast (template tag) : Prints a script AST 8 | astm (template tag) : Prints a module AST 9 | esparse : The full library API 10 | parse : Parses JS and returns a ParseResult 11 | `; 12 | 13 | global.esparse = esparse; 14 | 15 | global.parse = esparse.parse; 16 | 17 | function printAST(input, options) { 18 | let result = esparse.parse(input, options); 19 | console.log(util.inspect(result.ast, { 20 | colors: true, 21 | depth: 50, 22 | })); 23 | } 24 | 25 | Object.defineProperty(global, 'help', { 26 | get() { console.log(HELP) } 27 | }); 28 | 29 | global.ast = function(strings, ...values) { 30 | printAST(String.raw(strings, ...values)); 31 | }; 32 | 33 | global.astm = function(strings, ...values) { 34 | printAST(String.raw(strings, ...values), { module: true }); 35 | }; 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esparse # 2 | 3 | **esparse** is a parser for the ECMAScript programming language, written in 4 | JavaScript. Given a string representing a JavaScript program, it will output 5 | an abstract syntax tree representing that program. 6 | 7 | ## Project Goals ## 8 | 9 | The primary goal of this project is to implement an ECMAScript parser: 10 | 11 | - Whose source code is clear, easy to follow, and aesthetically pleasing. 12 | - That is easy to extend or modify for language experimentation purposes. 13 | - That provides an AST structure which is easy to traverse, easy to 14 | manipulate, and is convenient for a wide variety of code transformation 15 | use cases. 16 | - Whose performance is comparable to other modern ECMAScript parsers 17 | which are written in JavaScript. 18 | 19 | AST compatibility with other parsers is not a project goal. However, it 20 | is expected that any future changes to the AST protocol will be backward 21 | compatible with previous versions. 22 | -------------------------------------------------------------------------------- /test/parse/expressions/primary.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** null **/ 4 | "null": { 5 | type: "Script", 6 | statements: [ 7 | { type: "ExpressionStatement", 8 | expression: { 9 | type: "NullLiteral" 10 | } 11 | }] 12 | }, 13 | 14 | /** this **/ 15 | "this": { 16 | type: "Script", 17 | statements: [ 18 | { type: "ExpressionStatement", 19 | expression: { 20 | type: "ThisExpression" 21 | } 22 | }] 23 | }, 24 | 25 | /** true **/ 26 | "true": { 27 | type: "Script", 28 | statements: [ 29 | { type: "ExpressionStatement", 30 | expression: { 31 | type: "BooleanLiteral", 32 | value: true 33 | } 34 | }] 35 | }, 36 | 37 | /** false **/ 38 | "false": { 39 | type: "Script", 40 | statements: [ 41 | { type: "ExpressionStatement", 42 | expression: { 43 | type: "BooleanLiteral", 44 | value: false 45 | } 46 | }] 47 | }, 48 | 49 | }) 50 | -------------------------------------------------------------------------------- /test/parse/statements/let.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** let(); **/ 4 | "let is an identifier in non-strict code": { 5 | type: "Script", 6 | statements: [ 7 | 8 | { type: "ExpressionStatement", 9 | 10 | expression: 11 | { type: "CallExpression", 12 | callee: 13 | { type: "Identifier", 14 | value: "let" 15 | }, 16 | arguments: [], 17 | trailingComma: false, 18 | } 19 | }] 20 | }, 21 | 22 | /** "use strict"; let(); **/ 23 | "let is an invalid identifier in strict mode": {}, 24 | 25 | /** let x; **/ 26 | "basic let declaration": { 27 | type: "Script", 28 | statements: [ 29 | 30 | { type: "VariableDeclaration", 31 | kind: "let", 32 | declarations: [ 33 | { type: "VariableDeclarator", 34 | pattern: 35 | { type: "Identifier", 36 | value: "x" 37 | }, 38 | initializer: null 39 | }] 40 | }] 41 | } 42 | 43 | }) 44 | -------------------------------------------------------------------------------- /test/parse/expressions/array.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** x = [ 1, 2,, 3, ] **/ 4 | 'array literal with holes and trailing comma': 5 | { type: 'Script', 6 | start: 0, 7 | end: 17, 8 | statements: 9 | [ { type: 'ExpressionStatement', 10 | start: 0, 11 | end: 17, 12 | expression: 13 | { type: 'AssignmentExpression', 14 | start: 0, 15 | end: 17, 16 | operator: '=', 17 | left: 18 | { type: 'Identifier', 19 | start: 0, 20 | end: 1, 21 | value: 'x', 22 | context: 'variable' }, 23 | right: 24 | { type: 'ArrayLiteral', 25 | start: 4, 26 | end: 17, 27 | elements: 28 | [ { type: 'NumberLiteral', start: 6, end: 7, value: 1 }, 29 | { type: 'NumberLiteral', start: 9, end: 10, value: 2 }, 30 | null, 31 | { type: 'NumberLiteral', start: 13, end: 14, value: 3 } ], 32 | trailingComma: true } } } ] }, 33 | 34 | }) 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 zenparsing (Kevin Smith) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 17 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /test/parse/statements/switch.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** switch (x) { case 1: let x; } **/ 4 | "case clauses can have declarations": 5 | { type: 'Script', 6 | start: 0, 7 | end: 29, 8 | statements: 9 | [ { type: 'SwitchStatement', 10 | start: 0, 11 | end: 29, 12 | descriminant: 13 | { type: 'Identifier', 14 | start: 8, 15 | end: 9, 16 | value: 'x', 17 | context: 'variable' }, 18 | cases: 19 | [ { type: 'SwitchCase', 20 | start: 13, 21 | end: 27, 22 | test: { type: 'NumberLiteral', start: 18, end: 19, value: 1 }, 23 | consequent: 24 | [ { type: 'VariableDeclaration', 25 | start: 21, 26 | end: 27, 27 | kind: 'let', 28 | declarations: 29 | [ { type: 'VariableDeclarator', 30 | start: 25, 31 | end: 26, 32 | pattern: 33 | { type: 'Identifier', 34 | start: 25, 35 | end: 26, 36 | value: 'x', 37 | context: 'declaration' }, 38 | initializer: null } ] } ] } ] } ] }, 39 | 40 | }) 41 | -------------------------------------------------------------------------------- /test/parse/expressions/regular-expressions.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** /abc/i **/ 4 | 'a basic regular expression': 5 | { type: 'Script', 6 | start: 0, 7 | end: 6, 8 | statements: 9 | [ { type: 'ExpressionStatement', 10 | start: 0, 11 | end: 6, 12 | expression: 13 | { type: 'RegularExpression', 14 | start: 0, 15 | end: 6, 16 | value: 'abc', 17 | flags: 'i' } } ] }, 18 | 19 | /** /abc/ **/ 20 | 'a regular expression with no flags': 21 | { type: 'Script', 22 | start: 0, 23 | end: 5, 24 | statements: 25 | [ { type: 'ExpressionStatement', 26 | start: 0, 27 | end: 5, 28 | expression: 29 | { type: 'RegularExpression', 30 | start: 0, 31 | end: 5, 32 | value: 'abc', 33 | flags: '' } } ] }, 34 | 35 | /** /abc/000 **/ 36 | 'regular expression flags can start with a number': 37 | { type: 'Script', 38 | start: 0, 39 | end: 8, 40 | statements: 41 | [ { type: 'ExpressionStatement', 42 | start: 0, 43 | end: 8, 44 | expression: 45 | { type: 'RegularExpression', 46 | start: 0, 47 | end: 8, 48 | value: 'abc', 49 | flags: '000' } } ] }, 50 | 51 | /** /abc/i\u0065 **/ 52 | 'regular expression flags cannot contain unicode escapes': {}, 53 | 54 | }) 55 | -------------------------------------------------------------------------------- /test/parse/functions/defaults.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** function f(a = 1) { "use strict"; } **/ 4 | 'functions with non-simple params cannot have strict directive': {}, 5 | 6 | /** function f(a) { "use strict"; } **/ 7 | 'functions with simple params can have strict directive': { 8 | type: 'Script', 9 | start: 0, 10 | end: 31, 11 | statements: 12 | [ { 13 | type: 'FunctionDeclaration', 14 | start: 0, 15 | end: 31, 16 | kind: '', 17 | identifier: 18 | { 19 | type: 'Identifier', 20 | start: 9, 21 | end: 10, 22 | value: 'f', 23 | context: 'declaration' }, 24 | params: 25 | [ { 26 | type: 'FormalParameter', 27 | start: 11, 28 | end: 12, 29 | pattern: 30 | { 31 | type: 'Identifier', 32 | start: 11, 33 | end: 12, 34 | value: 'a', 35 | context: 'declaration' }, 36 | initializer: null } ], 37 | body: 38 | { 39 | type: 'FunctionBody', 40 | start: 14, 41 | end: 31, 42 | statements: 43 | [ { 44 | type: 'Directive', 45 | start: 16, 46 | end: 29, 47 | value: 'use strict', 48 | expression: 49 | { type: 'StringLiteral', start: 16, end: 28, value: 'use strict' } } ] } } ] }, 50 | 51 | }) 52 | -------------------------------------------------------------------------------- /test/print/cases/statements.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** if (x) { y; } else { z; } **/ 4 | 'if-then-else': 5 | `if (x) { 6 | y; 7 | } else { 8 | z; 9 | }`, 10 | 11 | /** switch (x) { case 1: a; break; case 2: b; break; default: break; } **/ 12 | 'switch-cases': 13 | `switch (x) { 14 | case 1: 15 | a; 16 | break; 17 | case 2: 18 | b; 19 | break; 20 | default: 21 | break; 22 | }`, 23 | 24 | /** try { x; } catch (e) { y; } **/ 25 | 'try-catch': 26 | `try { 27 | x; 28 | } catch (e) { 29 | y; 30 | }`, 31 | 32 | /** try { x; } catch (e) { y; } finally { z; } **/ 33 | 'try-catch-finally': 34 | `try { 35 | x; 36 | } catch (e) { 37 | y; 38 | } finally { 39 | z; 40 | }`, 41 | 42 | /** try { x; } finally { z; } **/ 43 | 'try-finally': 44 | `try { 45 | x; 46 | } finally { 47 | z; 48 | }`, 49 | 50 | /** for (let { x, y } of z); **/ 51 | 'for-of': 52 | `for (let { x, y } of z) ;`, 53 | 54 | /** if (x) {} else {} **/ 55 | 'if-else': 56 | `if (x) {} else {}`, 57 | 58 | /** if (x) 1; else 2; **/ 59 | 'if-else-short': 60 | `if (x) 1; 61 | else 2;`, 62 | 63 | /** switch (x) { case 1: case 2: case 3: a; } **/ 64 | 'switch-case-empty-case': 65 | `switch (x) { 66 | case 1: 67 | case 2: 68 | case 3: 69 | a; 70 | }`, 71 | 72 | /** 'use strict'; 1; **/ 73 | 'use strict directive': 74 | `'use strict'; 75 | 76 | 1;`, 77 | 78 | /** async function f() { for await (let x of y); } **/ 79 | 'for-await': 80 | `async function f() { 81 | for await (let x of y) ; 82 | }`, 83 | 84 | }) 85 | -------------------------------------------------------------------------------- /test/parse/expressions/binary.js: -------------------------------------------------------------------------------- 1 | [ 2 | 3 | /** 4 + 5 << (6) **/ 4 | { type: "Script", 5 | statements: [ 6 | 7 | { type: "ExpressionStatement", 8 | expression: 9 | 10 | { type: "BinaryExpression", 11 | operator: "<<", 12 | left: 13 | { type: "BinaryExpression", 14 | operator: "+", 15 | left: 16 | { type: "NumberLiteral", 17 | value: 4 18 | }, 19 | right: 20 | { type: "NumberLiteral", 21 | value: 5 22 | } 23 | }, 24 | 25 | right: 26 | { type: "ParenExpression", 27 | expression: 28 | { type: "NumberLiteral", 29 | value: 6 30 | } 31 | } 32 | } 33 | }] 34 | }, 35 | 36 | /** 1+-1 **/ 37 | { type: "Script", 38 | statements: [ 39 | { type: "ExpressionStatement", 40 | expression: 41 | { type: "BinaryExpression", 42 | operator: "+", 43 | left: 44 | { type: "NumberLiteral", 45 | value: 1 46 | }, 47 | right: 48 | { type: "UnaryExpression", 49 | operator: "-", 50 | expression: 51 | { type: "NumberLiteral", 52 | value: 1 53 | } 54 | } 55 | } 56 | }] 57 | }, 58 | 59 | ]; 60 | -------------------------------------------------------------------------------- /test/parse/expressions/assignment.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** x = y; **/ 4 | 'Basic assignment': { 5 | type: "Script", 6 | statements: [ 7 | 8 | { type: "ExpressionStatement", 9 | expression: 10 | 11 | { type: "AssignmentExpression", 12 | operator: "=", 13 | 14 | left: 15 | { type: "Identifier", 16 | value: "x" 17 | }, 18 | 19 | right: 20 | { type: "Identifier", 21 | value: "y" 22 | } 23 | } 24 | }] 25 | }, 26 | 27 | /** ((x)) = y; **/ 28 | 'LHS parens are unwrapped': 29 | { 30 | type: 'Script', 31 | start: 0, 32 | end: 10, 33 | statements: 34 | [ { 35 | type: 'ExpressionStatement', 36 | start: 0, 37 | end: 10, 38 | expression: 39 | { 40 | type: 'AssignmentExpression', 41 | start: 0, 42 | end: 9, 43 | operator: '=', 44 | left: 45 | { 46 | type: 'ParenExpression', 47 | start: 0, 48 | end: 5, 49 | expression: 50 | { 51 | type: 'ParenExpression', 52 | start: 1, 53 | end: 4, 54 | expression: 55 | { 56 | type: 'Identifier', 57 | start: 2, 58 | end: 3, 59 | value: 'x', 60 | context: 'variable' } } }, 61 | right: 62 | { 63 | type: 'Identifier', 64 | start: 8, 65 | end: 9, 66 | value: 'y', 67 | context: 'variable' } } } ] }, 68 | 69 | }) 70 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": ["eslint:recommended"], 3 | 4 | "env": { 5 | "es6": true 6 | }, 7 | 8 | "globals": { 9 | "window": true, 10 | "console": true 11 | }, 12 | 13 | "rules": { 14 | "no-console": ["error", { "allow": ["warn", "error"] }], 15 | "no-cond-assign": ["off"], 16 | "no-constant-condition": ["off"], 17 | "camelcase": ["error", { "properties": "always" }], 18 | "brace-style": ["error", "1tbs", { "allowSingleLine": true }], 19 | "eqeqeq": ["error", "smart"], 20 | "indent": ["error", 2, { "SwitchCase": 1 }], 21 | "no-throw-literal": ["error"], 22 | "comma-spacing": ["error", { "before": false, "after": true }], 23 | "comma-style": ["error", "last"], 24 | "comma-dangle": ["error", "always-multiline"], 25 | "keyword-spacing": ["error"], 26 | "no-trailing-spaces": ["error"], 27 | "no-multi-spaces": ["error"], 28 | "no-spaced-func": ["error"], 29 | "no-whitespace-before-property": ["error"], 30 | "space-before-blocks": ["error"], 31 | "space-before-function-paren": ["error", "never"], 32 | "space-in-parens": ["error", "never"], 33 | "eol-last": ["error"], 34 | "quotes": ["error", "single", { "avoidEscape": true }], 35 | "no-implicit-globals": ["error"], 36 | "no-useless-concat": ["error"], 37 | "space-infix-ops": ["error", { "int32Hint": true }], 38 | "semi-spacing": ["error", { "before": false, "after": true }], 39 | "semi": ["error", "always"], 40 | "object-curly-spacing": ["error", "always"], 41 | "array-bracket-spacing": ["error"], 42 | "max-len": ["error", 100] 43 | }, 44 | 45 | "parserOptions": { 46 | "ecmaVersion": 6, 47 | "sourceType": "module", 48 | "ecmaFeatures": { 49 | "jsx": true 50 | } 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /src/Unicode.js: -------------------------------------------------------------------------------- 1 | import { IDENTIFIER, WHITESPACE } from './UnicodeData.js'; 2 | 3 | function binarySearch(table, val) { 4 | let right = (table.length / 3) - 1; 5 | let left = 0; 6 | let mid = 0; 7 | let test = 0; 8 | let offset = 0; 9 | 10 | while (left <= right) { 11 | mid = (left + right) >> 1; 12 | offset = mid * 3; 13 | test = table[offset]; 14 | 15 | if (val < test) { 16 | right = mid - 1; 17 | } else if (val === test || val <= test + table[offset + 1]) { 18 | return table[offset + 2]; 19 | } else { 20 | left = mid + 1; 21 | } 22 | } 23 | 24 | return 0; 25 | } 26 | 27 | export function isIdentifierStart(code) { 28 | return binarySearch(IDENTIFIER, code) === 2; 29 | } 30 | 31 | export function isIdentifierPart(code) { 32 | return binarySearch(IDENTIFIER, code) >= 2; 33 | } 34 | 35 | export function isWhitespace(code) { 36 | return binarySearch(WHITESPACE, code) === 1; 37 | } 38 | 39 | export function codePointLength(code) { 40 | return code > 0xffff ? 2 : 1; 41 | } 42 | 43 | export function codePointAt(str, offset) { 44 | let a = str.charCodeAt(offset); 45 | 46 | if (a >= 0xd800 && a <= 0xdbff && str.length > offset + 1) { 47 | let b = str.charCodeAt(offset + 1); 48 | if (b >= 0xdc00 && b <= 0xdfff) 49 | return (a - 0xd800) * 0x400 + b - 0xdc00 + 0x10000; 50 | } 51 | 52 | return a; 53 | } 54 | 55 | export function codePointString(code) { 56 | if (code > 0x10ffff) 57 | return ''; 58 | 59 | if (code <= 0xffff) 60 | return String.fromCharCode(code); 61 | 62 | // If value is greater than 0xffff, then it must be encoded 63 | // as 2 UTF-16 code units in a surrogate pair. 64 | 65 | code -= 0x10000; 66 | 67 | return String.fromCharCode( 68 | (code >> 10) + 0xd800, 69 | (code % 0x400) + 0xdc00 70 | ); 71 | } 72 | -------------------------------------------------------------------------------- /test/parse/expressions/strings.js: -------------------------------------------------------------------------------- 1 | [ 2 | 3 | /** "hello"; **/ 4 | { type: "Script", 5 | statements: [ 6 | 7 | { type: "ExpressionStatement", 8 | expression: 9 | { type: "StringLiteral", 10 | value: "hello" 11 | } 12 | }] 13 | }, 14 | 15 | /** "\world"; **/ 16 | { type: "Script", 17 | statements: [ 18 | 19 | { type: "ExpressionStatement", 20 | expression: 21 | { type: "StringLiteral", 22 | value: "world" 23 | } 24 | }] 25 | }, 26 | 27 | /** "\r"; **/ 28 | { type: "Script", 29 | statements: [ 30 | 31 | { type: "ExpressionStatement", 32 | expression: 33 | { type: "StringLiteral", 34 | value: "\r" 35 | } 36 | }] 37 | }, 38 | 39 | /** "\n"; **/ 40 | { type: "Script", 41 | statements: [ 42 | 43 | { type: "ExpressionStatement", 44 | expression: 45 | { type: "StringLiteral", 46 | value: "\n" 47 | } 48 | }] 49 | }, 50 | 51 | /** "\t"; **/ 52 | { type: "Script", 53 | statements: [ 54 | 55 | { type: "ExpressionStatement", 56 | expression: 57 | { type: "StringLiteral", 58 | value: "\t" 59 | } 60 | }] 61 | }, 62 | 63 | /** "line\ 64 | continuation"; **/ 65 | { type: "Script", 66 | statements: [ 67 | 68 | { type: "ExpressionStatement", 69 | expression: 70 | { type: "StringLiteral", 71 | value: "linecontinuation" 72 | } 73 | }] 74 | }, 75 | 76 | /** "\101"; **/ 77 | { type: "Script", 78 | statements: [ 79 | 80 | { type: "ExpressionStatement", 81 | expression: 82 | { type: "StringLiteral", 83 | value: "A" 84 | } 85 | }] 86 | }, 87 | 88 | /** "use strict"; "\101"; **/ 89 | {}, 90 | 91 | ] 92 | -------------------------------------------------------------------------------- /test/parse/expressions/exponentiation.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** x ** y **/ 4 | "Exponentiation operator": { 5 | type: 'Script', 6 | start: 0, 7 | end: 6, 8 | statements: [{ 9 | type: 'ExpressionStatement', 10 | start: 0, 11 | end: 6, 12 | expression: { 13 | type: 'BinaryExpression', 14 | start: 0, 15 | end: 6, 16 | operator: '**', 17 | left: { 18 | type: 'Identifier', 19 | start: 0, 20 | end: 1, 21 | value: 'x', 22 | context: 'variable' }, 23 | right: { 24 | type: 'Identifier', 25 | start: 5, 26 | end: 6, 27 | value: 'y', 28 | context: 'variable' } } } ] }, 29 | 30 | /** -x ** y **/ 31 | "Unary expression are not allowed on LHS": {}, 32 | 33 | /** x ** -y **/ 34 | "Unary expression is allowed on RHS": { 35 | type: 'Script', 36 | start: 0, 37 | end: 7, 38 | statements: [{ 39 | type: 'ExpressionStatement', 40 | start: 0, 41 | end: 7, 42 | expression: { 43 | type: 'BinaryExpression', 44 | start: 0, 45 | end: 7, 46 | operator: '**', 47 | left: { 48 | type: 'Identifier', 49 | start: 0, 50 | end: 1, 51 | value: 'x', 52 | context: 'variable' }, 53 | right: { 54 | type: 'UnaryExpression', 55 | start: 5, 56 | end: 7, 57 | operator: '-', 58 | expression: { 59 | type: 'Identifier', 60 | start: 6, 61 | end: 7, 62 | value: 'y', 63 | context: 'variable' } } } } ] }, 64 | 65 | }) 66 | -------------------------------------------------------------------------------- /test/parse/functions/methods.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** ({ x() {} }); **/ 4 | 'object literal methods': 5 | { type: 'Script', 6 | start: 0, 7 | end: 13, 8 | statements: 9 | [ { type: 'ExpressionStatement', 10 | start: 0, 11 | end: 13, 12 | expression: 13 | { type: 'ParenExpression', 14 | start: 0, 15 | end: 12, 16 | expression: 17 | { type: 'ObjectLiteral', 18 | start: 1, 19 | end: 11, 20 | properties: 21 | [ { type: 'MethodDefinition', 22 | start: 3, 23 | end: 9, 24 | static: false, 25 | kind: '', 26 | name: { type: 'Identifier', start: 3, end: 4, value: 'x', context: '' }, 27 | params: [], 28 | body: { type: 'FunctionBody', start: 7, end: 9, statements: [] } } ], 29 | trailingComma: false } } } ] }, 30 | 31 | /** ({ *x() {} }); **/ 32 | 'generator method': 33 | { type: 'Script', 34 | start: 0, 35 | end: 14, 36 | statements: 37 | [ { type: 'ExpressionStatement', 38 | start: 0, 39 | end: 14, 40 | expression: 41 | { type: 'ParenExpression', 42 | start: 0, 43 | end: 13, 44 | expression: 45 | { type: 'ObjectLiteral', 46 | start: 1, 47 | end: 12, 48 | properties: 49 | [ { type: 'MethodDefinition', 50 | start: 3, 51 | end: 10, 52 | static: false, 53 | kind: 'generator', 54 | name: { type: 'Identifier', start: 4, end: 5, value: 'x', context: '' }, 55 | params: [], 56 | body: { type: 'FunctionBody', start: 8, end: 10, statements: [] } } ], 57 | trailingComma: false } } } ] }, 58 | 59 | }) 60 | -------------------------------------------------------------------------------- /test/scope/cases/catch.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** let e; try {} catch (e) { let e; } **/ 4 | 'catch parameters exist in a new scope': 5 | { type: 'var', 6 | names: {}, 7 | free: [], 8 | strict: false, 9 | children: 10 | [ { type: 'block', 11 | names: 12 | { e: 13 | { declarations: 14 | [ { type: 'Identifier', 15 | start: 4, 16 | end: 5, 17 | value: 'e', 18 | context: 'declaration' } ], 19 | references: [], 20 | const: false } }, 21 | free: [], 22 | strict: false, 23 | children: 24 | [ { type: 'block', 25 | names: {}, 26 | free: [], 27 | strict: false, 28 | children: [] }, 29 | { type: 'catch', 30 | names: 31 | { e: 32 | { declarations: 33 | [ { type: 'Identifier', 34 | start: 21, 35 | end: 22, 36 | value: 'e', 37 | context: 'declaration' } ], 38 | references: [], 39 | const: false } 40 | }, 41 | free: [], 42 | strict: false, 43 | children: [ 44 | { type: 'block', 45 | names: 46 | { e: 47 | { declarations: 48 | [ { type: 'Identifier', 49 | start: 30, 50 | end: 31, 51 | value: 'e', 52 | context: 'declaration' } ], 53 | references: [], 54 | const: false } }, 55 | free: [], 56 | strict: false, 57 | children: [] } ] } ] } ] }, 58 | 59 | }) 60 | -------------------------------------------------------------------------------- /test/parse/statements/async-for-of.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** async function f() { for await (let x of y); } **/ 4 | 'for await-of': 5 | { type: 'Script', 6 | start: 0, 7 | end: 46, 8 | statements: 9 | [ { type: 'FunctionDeclaration', 10 | start: 0, 11 | end: 46, 12 | kind: 'async', 13 | identifier: 14 | { type: 'Identifier', 15 | start: 15, 16 | end: 16, 17 | value: 'f', 18 | context: 'declaration' }, 19 | params: [], 20 | body: 21 | { type: 'FunctionBody', 22 | start: 19, 23 | end: 46, 24 | statements: 25 | [ { type: 'ForOfStatement', 26 | async: true, 27 | start: 21, 28 | end: 44, 29 | left: 30 | { type: 'VariableDeclaration', 31 | start: 32, 32 | end: 37, 33 | kind: 'let', 34 | declarations: 35 | [ { type: 'VariableDeclarator', 36 | start: 36, 37 | end: 37, 38 | pattern: 39 | { type: 'Identifier', 40 | start: 36, 41 | end: 37, 42 | value: 'x', 43 | context: 'declaration' }, 44 | initializer: null } ] }, 45 | right: 46 | { type: 'Identifier', 47 | start: 41, 48 | end: 42, 49 | value: 'y', 50 | context: 'variable' }, 51 | body: { type: 'EmptyStatement', start: 43, end: 44 } } ] } } ] }, 52 | 53 | /** for await (x of y); **/ 54 | 'for await-of only allowed in async context': 55 | {}, 56 | 57 | /** async function f() { for await (x in y); } **/ 58 | 'for await-in not allowed': 59 | {}, 60 | 61 | /** async function f() { for await (var i = 0; i < 10; ++i); } **/ 62 | 'for await-loop not allowed': 63 | {}, 64 | 65 | }) 66 | -------------------------------------------------------------------------------- /test/parse/functions/classic.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** function x() {} **/ 4 | "function declaration": { 5 | type: "Script", 6 | statements: [ 7 | { type: "FunctionDeclaration", 8 | kind: "", 9 | identifier: 10 | { type: "Identifier", 11 | value: "x", 12 | context: "declaration" 13 | }, 14 | params: [], 15 | body: 16 | { type: "FunctionBody", 17 | statements: [] 18 | } 19 | }] 20 | }, 21 | 22 | /** function yield() { "use strict" } **/ 23 | "function identifier must follow strictness of body": {}, 24 | 25 | /** function x(...args) { } **/ 26 | "function with rest parameter": { 27 | type: "Script", 28 | statements: [ 29 | { type: "FunctionDeclaration", 30 | kind: "", 31 | identifier: 32 | { type: "Identifier", 33 | value: "x", 34 | context: "declaration" 35 | }, 36 | params: [ 37 | { type: "RestParameter", 38 | identifier: 39 | { type: "Identifier", 40 | value: "args" 41 | } 42 | }], 43 | body: 44 | { type: "FunctionBody", 45 | statements: [] 46 | } 47 | }] 48 | }, 49 | 50 | /** function x(a, ...b) { } **/ 51 | "function with multiple parameters and a rest": { 52 | type: "Script", 53 | statements: [ 54 | { type: "FunctionDeclaration", 55 | kind: "", 56 | identifier: 57 | { type: "Identifier", 58 | value: "x", 59 | context: "declaration" 60 | }, 61 | params: [ 62 | { type: "FormalParameter", 63 | pattern: 64 | { type: "Identifier", 65 | value: "a" 66 | }, 67 | initializer: null 68 | }, 69 | { type: "RestParameter", 70 | identifier: 71 | { type: "Identifier", 72 | value: "b" 73 | } 74 | }], 75 | body: 76 | { type: "FunctionBody", 77 | statements: [] 78 | } 79 | }] 80 | }, 81 | 82 | /** function x(...b, c) { } **/ 83 | "a parameter cannot follow a rest": {}, 84 | 85 | }) -------------------------------------------------------------------------------- /test/scope/cases/var-shadowing.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** 4 | let x; 5 | { 6 | var x; 7 | } 8 | **/ 9 | 'var cannot shadow let': {}, 10 | 11 | /** 12 | 'use strict'; 13 | function f(x) { var x } 14 | **/ 15 | 'var can shadow function parameters': 16 | { type: 'var', 17 | names: 18 | { f: 19 | { declarations: 20 | [ { type: 'Identifier', 21 | start: 23, 22 | end: 24, 23 | value: 'f', 24 | context: 'declaration' } ], 25 | references: [], 26 | const: false } }, 27 | free: [], 28 | strict: true, 29 | children: 30 | [ { type: 'block', 31 | names: {}, 32 | free: [], 33 | strict: true, 34 | children: 35 | [ { type: 'function', 36 | names: {}, 37 | free: [], 38 | strict: true, 39 | children: 40 | [ { type: 'param', 41 | names: 42 | { x: 43 | { declarations: 44 | [ { type: 'Identifier', 45 | start: 25, 46 | end: 26, 47 | value: 'x', 48 | context: 'declaration' } ], 49 | references: [], 50 | const: false } }, 51 | free: [], 52 | strict: true, 53 | children: 54 | [ { type: 'var', 55 | names: 56 | { x: 57 | { declarations: 58 | [ { type: 'Identifier', 59 | start: 34, 60 | end: 35, 61 | value: 'x', 62 | context: 'declaration' } ], 63 | references: [], 64 | const: false } }, 65 | free: [], 66 | strict: true, 67 | children: 68 | [ { type: 'block', 69 | names: {}, 70 | free: [], 71 | strict: true, 72 | children: [] } ] } ] } ] } ] } ] }, 73 | 74 | }) 75 | -------------------------------------------------------------------------------- /test/scope/cases/params.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** 4 | function x(a, a) {} 5 | **/ 6 | 'duplicates allowed in simple param lists': 7 | { type: 'var', 8 | names: 9 | { x: 10 | { declarations: 11 | [ { type: 'Identifier', 12 | start: 9, 13 | end: 10, 14 | value: 'x', 15 | context: 'declaration' } ], 16 | references: [], 17 | const: false } }, 18 | free: [], 19 | strict: false, 20 | children: 21 | [ { type: 'block', 22 | names: {}, 23 | free: [], 24 | strict: false, 25 | children: 26 | [ { type: 'function', 27 | names: {}, 28 | free: [], 29 | strict: false, 30 | children: 31 | [ { type: 'param', 32 | names: 33 | { a: 34 | { declarations: 35 | [ { type: 'Identifier', 36 | start: 11, 37 | end: 12, 38 | value: 'a', 39 | context: 'declaration' }, 40 | { type: 'Identifier', 41 | start: 14, 42 | end: 15, 43 | value: 'a', 44 | context: 'declaration' } ], 45 | references: [], 46 | const: false, } }, 47 | free: [], 48 | strict: false, 49 | children: 50 | [ { type: 'var', 51 | names: {}, 52 | free: [], 53 | strict: false, 54 | children: 55 | [ { type: 'block', 56 | names: {}, 57 | free: [], 58 | strict: false, 59 | children: [] } ] } ] } ] } ] } ] }, 60 | 61 | /** 62 | function x(a, a) { 'use strict' } 63 | **/ 64 | 'duplicates not allowed in strict mode': {}, 65 | 66 | /** 67 | function x(a = function(a, a) {}) { 'use strict' } 68 | **/ 69 | 'duplicates not allowed in strict mode with default nesting': {}, 70 | 71 | /** 72 | function x(a, ...a) {} 73 | **/ 74 | 'duplicates not allowed with a rest parameter': {}, 75 | 76 | /** 77 | function x(a, [a]) {} 78 | **/ 79 | 'duplicates not allowed with array destructuring': {}, 80 | 81 | /** 82 | function x(a, {b:a}) {} 83 | **/ 84 | 'duplicates not allowed with object destructuring': {}, 85 | 86 | /** 87 | function x(a) { let a } 88 | **/ 89 | 'block scope declarations cannot shadow parameter names': {}, 90 | 91 | 92 | }) 93 | -------------------------------------------------------------------------------- /test/scope/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | 13.1.1 (Block) 4 | - no duplicate lexical names in body 5 | - lexical names cannot conflict with var names in body 6 | 13.2.1.1 (Lexical Declarations) 7 | - 'let' cannot be a binding variable name 8 | - no duplicate binding names in lexical declarations 9 | 13.6.3.1 (For Head) 10 | - var names can't shadow lexical names in head 11 | 13.6.4.1 (ForIn/Of Head) 12 | - 'let' cannot be a binding variable name 13 | - no duplicate binding names in lexical declarations 14 | - var names can't shadown lexical names in head 15 | 13.11.1 (Case Block) 16 | - no duplicate lexical names in case clauses 17 | - lexical names cannot conflict with var names in case clauses 18 | 13.14.1 (Catch Block) 19 | - catch parameter cannot conflict with lexical names in body 20 | - catch parameter cannot conflict with var names in body 21 | 14.1.2 (Functions) 22 | - no conflicts between param names and lexical names of body 23 | - (strict or non-simple parameters) no duplicate parameter names 24 | - lexical names of body cannot have duplicates 25 | - lexical names cannot conflict with var names in body 26 | 14.2.1 (Arrows) 27 | - no conflicts between param names and lexical names of body 28 | - no duplicate parameter names 29 | - lexical names of body cannot have duplicates 30 | - lexical names cannot conflict with var names in body 31 | 14.3.1 (Methods) 32 | - same rules as arrows 33 | 14.4.1 (Generators) 34 | - Same rules as functions 35 | 15.1.1 (Script) 36 | - Same as block 37 | 15.2.1.1 (ModuleBody) 38 | - Same as block 39 | 15.2.2.1 (ModuleItem) 40 | - Imported bindings cannot contain duplicates 41 | 42 | */ 43 | 44 | const { parse, resolveScopes } = require('../../src'); 45 | const { runTests, objectLike } = require('../runner.js'); 46 | 47 | function process(source, options) { 48 | let result = parse(source, options); 49 | let scope = resolveScopes(result.ast, result); 50 | return cleanResult(scope); 51 | } 52 | 53 | function cleanResult(x) { 54 | if (!x || typeof x !== 'object') 55 | return x; 56 | 57 | if (x instanceof Map) { 58 | let obj = {}; 59 | x.forEach((value, key) => obj[key] = value); 60 | return obj; 61 | } 62 | 63 | for (let k of Object.keys(x)) { 64 | switch (k) { 65 | case 'node': 66 | case 'parent': 67 | delete x[k]; 68 | break; 69 | default: 70 | x[k] = cleanResult(x[k]); 71 | break; 72 | } 73 | } 74 | 75 | return x; 76 | } 77 | 78 | function compare(a, b) { 79 | return objectLike(a, b, ['node', 'message', 'parent', 'start', 'end']); 80 | } 81 | 82 | runTests({ 83 | dir: __dirname, 84 | process, 85 | compare, 86 | }); 87 | -------------------------------------------------------------------------------- /test/annotations/cases/class.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** @A class C {} **/ 4 | 'class declaration': 5 | [ { 6 | type: 'Annotation', 7 | start: 0, 8 | end: 2, 9 | path: 10 | [ { 11 | type: 'Identifier', 12 | start: 1, 13 | end: 2, 14 | value: 'A', 15 | context: '' } ], 16 | arguments: null } ], 17 | 18 | /** @A.B class C {} **/ 19 | 'with path': 20 | [ { 21 | type: 'Annotation', 22 | start: 0, 23 | end: 4, 24 | path: 25 | [ { 26 | type: 'Identifier', 27 | start: 1, 28 | end: 2, 29 | value: 'A', 30 | context: '' }, 31 | { type: 'Identifier', start: 3, end: 4, value: 'B', context: '' } ], 32 | arguments: null } ], 33 | 34 | /** @A(x) class C {} **/ 35 | 'with arguments': 36 | [ { 37 | type: 'Annotation', 38 | start: 0, 39 | end: 5, 40 | path: 41 | [ { 42 | type: 'Identifier', 43 | start: 1, 44 | end: 2, 45 | value: 'A', 46 | context: '' } ], 47 | arguments: 48 | [ { 49 | type: 'Identifier', 50 | start: 3, 51 | end: 4, 52 | value: 'x', 53 | context: 'variable' } ] } ], 54 | 55 | /** @A @B class C {} **/ 56 | 'multiple annotations': 57 | [ { 58 | type: 'Annotation', 59 | start: 0, 60 | end: 2, 61 | path: 62 | [ { 63 | type: 'Identifier', 64 | start: 1, 65 | end: 2, 66 | value: 'A', 67 | context: '' } ], 68 | arguments: null }, 69 | { 70 | type: 'Annotation', 71 | start: 3, 72 | end: 5, 73 | path: 74 | [ { 75 | type: 'Identifier', 76 | start: 4, 77 | end: 5, 78 | value: 'B', 79 | context: '' } ], 80 | arguments: null } ], 81 | 82 | /** @private x **/ 83 | 'reserved words are allowed': [ { 84 | type: 'Annotation', 85 | start: 0, 86 | end: 8, 87 | path: 88 | [ { 89 | type: 'Identifier', 90 | start: 1, 91 | end: 8, 92 | value: 'private', 93 | context: '' } ], 94 | arguments: null } ], 95 | 96 | /** @x 'abc' 97 | @y 'def' **/ 98 | 'ASI works with annotations': [ { 99 | type: 'Annotation', 100 | start: 0, 101 | end: 2, 102 | path: 103 | [ { type: 'Identifier', start: 1, end: 2, value: 'x', context: '' } ], 104 | arguments: null }, { 105 | type: 'Annotation', 106 | start: 13, 107 | end: 15, 108 | path: 109 | [ { type: 'Identifier', start: 14, end: 15, value: 'y', context: '' } ], 110 | arguments: null } ], 111 | 112 | /** { @x } **/ 113 | 'not allowed before closing brace': {}, 114 | 115 | /** a; @x **/ 116 | 'not allowed before script end': {}, 117 | 118 | /*** a; @x ***/ 119 | 'not allowed before module end': {}, 120 | 121 | }) 122 | -------------------------------------------------------------------------------- /test/parse/functions/rest.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** function f(...args) {} **/ 4 | 'single rest parameter': 5 | { type: 'Script', 6 | start: 0, 7 | end: 22, 8 | statements: 9 | [ { type: 'FunctionDeclaration', 10 | start: 0, 11 | end: 22, 12 | kind: '', 13 | identifier: 14 | { type: 'Identifier', 15 | start: 9, 16 | end: 10, 17 | value: 'f', 18 | context: 'declaration' }, 19 | params: 20 | [ { type: 'RestParameter', 21 | start: 11, 22 | end: 18, 23 | identifier: 24 | { type: 'Identifier', 25 | start: 14, 26 | end: 18, 27 | value: 'args', 28 | context: 'declaration' } } ], 29 | body: { type: 'FunctionBody', start: 20, end: 22, statements: [] } } ] }, 30 | 31 | /** function f(a, b, ...[c]) {} **/ 32 | 'rest parameter cannot be a pattern': 33 | { 34 | type: 'Script', 35 | start: 0, 36 | end: 27, 37 | statements: 38 | [ { 39 | type: 'FunctionDeclaration', 40 | start: 0, 41 | end: 27, 42 | kind: '', 43 | identifier: 44 | { 45 | type: 'Identifier', 46 | start: 9, 47 | end: 10, 48 | value: 'f', 49 | context: 'declaration' }, 50 | params: 51 | [ { 52 | type: 'FormalParameter', 53 | start: 11, 54 | end: 12, 55 | pattern: 56 | { 57 | type: 'Identifier', 58 | start: 11, 59 | end: 12, 60 | value: 'a', 61 | context: 'declaration' }, 62 | initializer: null }, 63 | { 64 | type: 'FormalParameter', 65 | start: 14, 66 | end: 15, 67 | pattern: 68 | { 69 | type: 'Identifier', 70 | start: 14, 71 | end: 15, 72 | value: 'b', 73 | context: 'declaration' }, 74 | initializer: null }, 75 | { 76 | type: 'RestParameter', 77 | start: 17, 78 | end: 23, 79 | identifier: 80 | { 81 | type: 'ArrayPattern', 82 | start: 20, 83 | end: 23, 84 | elements: 85 | [ { 86 | type: 'PatternElement', 87 | start: 21, 88 | end: 22, 89 | pattern: 90 | { 91 | type: 'Identifier', 92 | start: 21, 93 | end: 22, 94 | value: 'c', 95 | context: 'declaration' }, 96 | initializer: null } ], 97 | trailingComma: false } } ], 98 | body: 99 | { type: 'FunctionBody', start: 25, end: 27, statements: [] } } ] }, 100 | 101 | /** function f(a, ...b, c) {} **/ 102 | 'rest parameter can only occur as the last formal parameter': 103 | {}, 104 | 105 | }) 106 | -------------------------------------------------------------------------------- /test/parse/classes/class-fields.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** class C { foo; } **/ 4 | 'Empty class field': 5 | { 6 | type: 'Script', 7 | start: 0, 8 | end: 16, 9 | statements: 10 | [ { 11 | type: 'ClassDeclaration', 12 | start: 0, 13 | end: 16, 14 | identifier: 15 | { 16 | type: 'Identifier', 17 | start: 6, 18 | end: 7, 19 | value: 'C', 20 | context: 'declaration' }, 21 | base: null, 22 | body: 23 | { 24 | type: 'ClassBody', 25 | start: 8, 26 | end: 16, 27 | elements: 28 | [ { 29 | type: 'ClassField', 30 | static: false, 31 | name: 32 | { 33 | type: 'Identifier', 34 | start: 10, 35 | end: 13, 36 | value: 'foo', 37 | context: '' }, 38 | initializer: null, 39 | start: 10, 40 | end: 14 } ] } } ] }, 41 | 42 | /** class C { foo 43 | bar 44 | static baz 45 | } **/ 46 | 'Empty class fields support ASI': 47 | { 48 | type: 'Script', 49 | start: 0, 50 | end: 34, 51 | statements: 52 | [ { 53 | type: 'ClassDeclaration', 54 | start: 0, 55 | end: 34, 56 | identifier: 57 | { 58 | type: 'Identifier', 59 | start: 6, 60 | end: 7, 61 | value: 'C', 62 | context: 'declaration' }, 63 | base: null, 64 | body: 65 | { 66 | type: 'ClassBody', 67 | start: 8, 68 | end: 34, 69 | elements: 70 | [ { 71 | type: 'ClassField', 72 | static: false, 73 | name: 74 | { 75 | type: 'Identifier', 76 | start: 10, 77 | end: 13, 78 | value: 'foo', 79 | context: '' }, 80 | initializer: null, 81 | start: 10, 82 | end: 13 }, 83 | { 84 | type: 'ClassField', 85 | static: false, 86 | name: 87 | { 88 | type: 'Identifier', 89 | start: 16, 90 | end: 19, 91 | value: 'bar', 92 | context: '' }, 93 | initializer: null, 94 | start: 16, 95 | end: 19 }, 96 | { 97 | type: 'ClassField', 98 | static: true, 99 | name: 100 | { 101 | type: 'Identifier', 102 | start: 29, 103 | end: 32, 104 | value: 'baz', 105 | context: '' }, 106 | initializer: null, 107 | start: 22, 108 | end: 32 } ] } } ] }, 109 | 110 | /** class C { static constructor } **/ 111 | 'Static constructor field not allowed': {}, 112 | 113 | /** class C { static prototype } **/ 114 | 'Static prototype field not allowed': {}, 115 | 116 | /** class C { constructor } **/ 117 | 'Instance constructor field not allowed': {}, 118 | 119 | }) 120 | -------------------------------------------------------------------------------- /test/parse/unicode/unicode.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** "ab\xB5cd"; **/ 4 | 'hex escape in string': 5 | { type: 'Script', 6 | start: 0, 7 | end: 11, 8 | statements: 9 | [ { type: 'ExpressionStatement', 10 | start: 0, 11 | end: 11, 12 | expression: { type: 'StringLiteral', start: 0, end: 10, value: 'abµcd' } } ] }, 13 | 14 | /** "ab\u01BBcd"; **/ 15 | 'unicode escape in string': 16 | { type: 'Script', 17 | start: 0, 18 | end: 13, 19 | statements: 20 | [ { type: 'ExpressionStatement', 21 | start: 0, 22 | end: 13, 23 | expression: { type: 'StringLiteral', start: 0, end: 12, value: 'abƻcd' } } ] }, 24 | 25 | /** \u{64}efg; **/ 26 | "extended unicode escapes - 1": { 27 | type: "Script", 28 | statements: [ 29 | { type: "ExpressionStatement", 30 | expression: { 31 | type: "Identifier", 32 | value: "defg" 33 | } 34 | }] 35 | }, 36 | 37 | /** gfe\u{64}; **/ 38 | "extended unicode escapes - 2": { 39 | type: "Script", 40 | statements: [ 41 | { type: "ExpressionStatement", 42 | expression: { 43 | type: "Identifier", 44 | value: "gfed" 45 | } 46 | }] 47 | }, 48 | 49 | /** ƒ; **/ 50 | "unicode identifiers": { 51 | type: "Script", 52 | statements: [ 53 | { type: "ExpressionStatement", 54 | expression: { 55 | type: "Identifier", 56 | value: "ƒ" 57 | } 58 | }] 59 | }, 60 | 61 | /** "\u{2f804}" **/ 62 | 'non-bmp unicode escapes': 63 | { type: 'Script', 64 | start: 0, 65 | end: 11, 66 | statements: 67 | [ { type: 'ExpressionStatement', 68 | start: 0, 69 | end: 11, 70 | expression: { type: 'StringLiteral', start: 0, end: 11, value: '\ud87e\udc04' } } ] }, 71 | 72 | /** \u{1D4A2} **/ 73 | 'non-bmp unicode escapes in identifiers': 74 | { type: 'Script', 75 | start: 0, 76 | end: 9, 77 | statements: 78 | [ { type: 'ExpressionStatement', 79 | start: 0, 80 | end: 9, 81 | expression: 82 | { type: 'Identifier', 83 | start: 0, 84 | end: 9, 85 | value: '𝒢', 86 | context: 'variable' } } ] }, 87 | 88 | /** 𝒢; **/ 89 | 'non-bmp identifier characters': 90 | { type: 'Script', 91 | start: 0, 92 | end: 3, 93 | statements: 94 | [ { type: 'ExpressionStatement', 95 | start: 0, 96 | end: 3, 97 | expression: 98 | { type: 'Identifier', 99 | start: 0, 100 | end: 2, 101 | value: '𝒢', 102 | context: 'variable' } } ] }, 103 | 104 | /** 9𝒢; **/ 105 | 'number cannot be followed by a non-BMP identifier start character': {}, 106 | 107 | /** \u{} **/ 108 | 'unicode escapes must have a valid hex value - 1': {}, 109 | 110 | /** \u{hah} **/ 111 | 'unicode escapes must have a valid hex value - 2': {}, 112 | 113 | /** \u{ **/ 114 | 'unicode escapes must have balanced braces': {}, 115 | 116 | /** \u{12 } **/ 117 | 'unicode escapes cannot use space': {}, 118 | 119 | /** \u0030abc; **/ 120 | 'cannot use escape sequence to create invalid identifiers': {}, 121 | 122 | /** "\u{110000}" **/ 123 | 'unicode escapes cannot be greater than 0x10ffff': {}, 124 | 125 | /** \u0064ebugger; **/ 126 | 'reserved words cannot contain unicode escapes': {}, 127 | 128 | /** l\u0065t x = 1; **/ 129 | 'contextual keywords cannot contain unicode escapes - 1': {}, 130 | 131 | /** a\u0073ync function af() {} **/ 132 | 'contextual keywords cannot contain unicode escapes - 2': {}, 133 | 134 | 135 | }) 136 | -------------------------------------------------------------------------------- /test/parse/expressions/spread.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** f(...a) **/ 4 | 'spread in function call': 5 | { type: 'Script', 6 | start: 0, 7 | end: 7, 8 | statements: 9 | [ { type: 'ExpressionStatement', 10 | start: 0, 11 | end: 7, 12 | expression: 13 | { type: 'CallExpression', 14 | start: 0, 15 | end: 7, 16 | callee: 17 | { type: 'Identifier', 18 | start: 0, 19 | end: 1, 20 | value: 'f', 21 | context: 'variable' }, 22 | arguments: 23 | [ { type: 'SpreadExpression', 24 | start: 2, 25 | end: 6, 26 | expression: 27 | { type: 'Identifier', 28 | start: 5, 29 | end: 6, 30 | value: 'a', 31 | context: 'variable' } } ], 32 | trailingComma: false } } ] }, 33 | 34 | /** f(a, ...1 + 1, b) **/ 35 | 'spread allows any arbitrary expression': 36 | { type: 'Script', 37 | start: 0, 38 | end: 17, 39 | statements: 40 | [ { type: 'ExpressionStatement', 41 | start: 0, 42 | end: 17, 43 | expression: 44 | { type: 'CallExpression', 45 | start: 0, 46 | end: 17, 47 | callee: 48 | { type: 'Identifier', 49 | start: 0, 50 | end: 1, 51 | value: 'f', 52 | context: 'variable' }, 53 | arguments: 54 | [ { type: 'Identifier', 55 | start: 2, 56 | end: 3, 57 | value: 'a', 58 | context: 'variable' }, 59 | { type: 'SpreadExpression', 60 | start: 5, 61 | end: 13, 62 | expression: 63 | { type: 'BinaryExpression', 64 | start: 8, 65 | end: 13, 66 | operator: '+', 67 | left: { type: 'NumberLiteral', start: 8, end: 9, value: 1 }, 68 | right: { type: 'NumberLiteral', start: 12, end: 13, value: 1 } } }, 69 | { type: 'Identifier', 70 | start: 15, 71 | end: 16, 72 | value: 'b', 73 | context: 'variable' } ], 74 | trailingComma: false } } ] }, 75 | 76 | /** [...a] **/ 77 | 'spread in an array literal': 78 | { type: 'Script', 79 | start: 0, 80 | end: 6, 81 | statements: 82 | [ { type: 'ExpressionStatement', 83 | start: 0, 84 | end: 6, 85 | expression: 86 | { type: 'ArrayLiteral', 87 | start: 0, 88 | end: 6, 89 | elements: 90 | [ { type: 'SpreadExpression', 91 | start: 1, 92 | end: 5, 93 | expression: 94 | { type: 'Identifier', 95 | start: 4, 96 | end: 5, 97 | value: 'a', 98 | context: 'variable' } } ], 99 | trailingComma: false } } ] }, 100 | 101 | /** [...a] = [] **/ 102 | 'spread in array assignment pattern': 103 | { type: 'Script', 104 | start: 0, 105 | end: 11, 106 | statements: 107 | [ { type: 'ExpressionStatement', 108 | start: 0, 109 | end: 11, 110 | expression: 111 | { type: 'AssignmentExpression', 112 | start: 0, 113 | end: 11, 114 | operator: '=', 115 | left: 116 | { type: 'ArrayPattern', 117 | start: 0, 118 | end: 6, 119 | elements: 120 | [ { type: 'PatternRestElement', 121 | start: 1, 122 | end: 5, 123 | pattern: 124 | { type: 'Identifier', 125 | start: 4, 126 | end: 5, 127 | value: 'a', 128 | context: 'variable' } } ], 129 | trailingComma: false }, 130 | right: 131 | { type: 'ArrayLiteral', 132 | start: 9, 133 | end: 11, 134 | elements: [], 135 | trailingComma: false } } } ] }, 136 | 137 | /** (...a) **/ 138 | 'spread expressions are not allowed outside of arrays or function calls': 139 | {}, 140 | 141 | }) 142 | -------------------------------------------------------------------------------- /test/parse/functions/trailing-comma.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** function f(a, b,) {} **/ 4 | 'trailing comma allowed in formal parameters': { 5 | type: 'Script', 6 | start: 0, 7 | end: 20, 8 | statements: 9 | [ { 10 | type: 'FunctionDeclaration', 11 | start: 0, 12 | end: 20, 13 | kind: '', 14 | identifier: 15 | { 16 | type: 'Identifier', 17 | start: 9, 18 | end: 10, 19 | value: 'f', 20 | context: 'declaration' }, 21 | params: 22 | [ { 23 | type: 'FormalParameter', 24 | start: 11, 25 | end: 12, 26 | pattern: 27 | { 28 | type: 'Identifier', 29 | start: 11, 30 | end: 12, 31 | value: 'a', 32 | context: 'declaration' }, 33 | initializer: null }, 34 | { 35 | type: 'FormalParameter', 36 | start: 14, 37 | end: 15, 38 | pattern: 39 | { 40 | type: 'Identifier', 41 | start: 14, 42 | end: 15, 43 | value: 'b', 44 | context: 'declaration' }, 45 | initializer: null } ], 46 | body: { type: 'FunctionBody', start: 18, end: 20, statements: [] } } ] }, 47 | 48 | /** m(a,) **/ 49 | "trailing comma allowed in call expressions": { 50 | type: 'Script', 51 | start: 0, 52 | end: 5, 53 | statements: 54 | [ { 55 | type: 'ExpressionStatement', 56 | start: 0, 57 | end: 5, 58 | expression: 59 | { 60 | type: 'CallExpression', 61 | start: 0, 62 | end: 5, 63 | callee: 64 | { 65 | type: 'Identifier', 66 | start: 0, 67 | end: 1, 68 | value: 'm', 69 | context: 'variable' }, 70 | arguments: 71 | [ { 72 | type: 'Identifier', 73 | start: 2, 74 | end: 3, 75 | value: 'a', 76 | context: 'variable' } ], 77 | trailingComma: true } } ] }, 78 | 79 | /** (a,) => 1 **/ 80 | 'trailing comma allowed in arrow functions': { 81 | type: 'Script', 82 | start: 0, 83 | end: 9, 84 | statements: 85 | [ { 86 | type: 'ExpressionStatement', 87 | start: 0, 88 | end: 9, 89 | expression: 90 | { 91 | type: 'ArrowFunction', 92 | start: 0, 93 | end: 9, 94 | kind: '', 95 | params: 96 | [ { 97 | type: 'FormalParameter', 98 | start: 1, 99 | end: 2, 100 | pattern: 101 | { 102 | type: 'Identifier', 103 | start: 1, 104 | end: 2, 105 | value: 'a', 106 | context: 'declaration' }, 107 | initializer: null } ], 108 | body: { type: 'NumberLiteral', start: 8, end: 9, value: 1 } } } ] }, 109 | 110 | /** async (m,) => 1 **/ 111 | 'trailing comma allowed in async arrow function': { 112 | type: 'Script', 113 | start: 0, 114 | end: 15, 115 | statements: 116 | [ { 117 | type: 'ExpressionStatement', 118 | start: 0, 119 | end: 15, 120 | expression: 121 | { 122 | type: 'ArrowFunction', 123 | start: 0, 124 | end: 15, 125 | kind: 'async', 126 | params: 127 | [ { 128 | type: 'FormalParameter', 129 | start: 7, 130 | end: 8, 131 | pattern: 132 | { 133 | type: 'Identifier', 134 | start: 7, 135 | end: 8, 136 | value: 'm', 137 | context: 'declaration' }, 138 | initializer: null } ], 139 | body: { type: 'NumberLiteral', start: 14, end: 15, value: 1 } } } ] }, 140 | 141 | /** (x,); **/ 142 | 'trailing comma not allowed in paren expression': {}, 143 | 144 | /** function f(...x,) {} **/ 145 | 'trailing comma not allowed after rest parameter': {}, 146 | 147 | /** function f(,) {} **/ 148 | 'trailing comma not allowed in empty parameter list': {}, 149 | 150 | /** (,) => {} **/ 151 | 'trailing comma not allowed in arrow empty parameter list': {}, 152 | 153 | /** (...x,) => {} **/ 154 | 'trailing comma not allowed in arrow after rest parameter': {}, 155 | 156 | /** async (...x,) => {} **/ 157 | 'trailing comma not allowed in async arrow after rest parameter': {}, 158 | 159 | }); 160 | -------------------------------------------------------------------------------- /test/parse/destructuring/array-binding.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** var [a] = x; **/ 4 | 'basic array binding pattern': 5 | { type: 'Script', 6 | start: 0, 7 | end: 12, 8 | statements: 9 | [ { type: 'VariableDeclaration', 10 | start: 0, 11 | end: 12, 12 | kind: 'var', 13 | declarations: 14 | [ { type: 'VariableDeclarator', 15 | start: 4, 16 | end: 11, 17 | pattern: 18 | { type: 'ArrayPattern', 19 | start: 4, 20 | end: 7, 21 | elements: 22 | [ { type: 'PatternElement', 23 | start: 5, 24 | end: 6, 25 | pattern: 26 | { type: 'Identifier', 27 | start: 5, 28 | end: 6, 29 | value: 'a', 30 | context: 'declaration' }, 31 | initializer: null } ], 32 | trailingComma: false }, 33 | initializer: 34 | { type: 'Identifier', 35 | start: 10, 36 | end: 11, 37 | value: 'x', 38 | context: 'variable' } } ] } ] }, 39 | 40 | /** var [a]; **/ 41 | 'pattern must have an initializer': 42 | {}, 43 | 44 | /** var ([a]) = x; **/ 45 | 'parens are not unwrapped': 46 | {}, 47 | 48 | /** var [...a] = x; **/ 49 | 'rest target': 50 | { type: 'Script', 51 | start: 0, 52 | end: 15, 53 | statements: 54 | [ { type: 'VariableDeclaration', 55 | start: 0, 56 | end: 15, 57 | kind: 'var', 58 | declarations: 59 | [ { type: 'VariableDeclarator', 60 | start: 4, 61 | end: 14, 62 | pattern: 63 | { type: 'ArrayPattern', 64 | start: 4, 65 | end: 10, 66 | elements: 67 | [ { type: 'PatternRestElement', 68 | start: 5, 69 | end: 9, 70 | pattern: 71 | { type: 'Identifier', 72 | start: 8, 73 | end: 9, 74 | value: 'a', 75 | context: 'declaration' } } ], 76 | trailingComma: false }, 77 | initializer: 78 | { type: 'Identifier', 79 | start: 13, 80 | end: 14, 81 | value: 'x', 82 | context: 'variable' } } ] } ] }, 83 | 84 | /** var [...a, b] = x; **/ 85 | 'rest element must occur in last position': 86 | {}, 87 | 88 | /** var [...a,] = x; **/ 89 | 'rest element cannot be followed by a comma': 90 | {}, 91 | 92 | /** var [] = x **/ 93 | 'empty patterns are allowed': 94 | { type: 'Script', 95 | start: 0, 96 | end: 10, 97 | statements: 98 | [ { type: 'VariableDeclaration', 99 | start: 0, 100 | end: 10, 101 | kind: 'var', 102 | declarations: 103 | [ { type: 'VariableDeclarator', 104 | start: 4, 105 | end: 10, 106 | pattern: 107 | { type: 'ArrayPattern', 108 | start: 4, 109 | end: 6, 110 | elements: [], 111 | trailingComma: false }, 112 | initializer: 113 | { type: 'Identifier', 114 | start: 9, 115 | end: 10, 116 | value: 'x', 117 | context: 'variable' } } ] } ] }, 118 | 119 | /** var [, a, , b] = x **/ 120 | 'elisions are allowed': 121 | { type: 'Script', 122 | start: 0, 123 | end: 18, 124 | statements: 125 | [ { type: 'VariableDeclaration', 126 | start: 0, 127 | end: 18, 128 | kind: 'var', 129 | declarations: 130 | [ { type: 'VariableDeclarator', 131 | start: 4, 132 | end: 18, 133 | pattern: 134 | { type: 'ArrayPattern', 135 | start: 4, 136 | end: 14, 137 | elements: 138 | [ null, 139 | { type: 'PatternElement', 140 | start: 7, 141 | end: 8, 142 | pattern: 143 | { type: 'Identifier', 144 | start: 7, 145 | end: 8, 146 | value: 'a', 147 | context: 'declaration' }, 148 | initializer: null }, 149 | null, 150 | { type: 'PatternElement', 151 | start: 12, 152 | end: 13, 153 | pattern: 154 | { type: 'Identifier', 155 | start: 12, 156 | end: 13, 157 | value: 'b', 158 | context: 'declaration' }, 159 | initializer: null } ], 160 | trailingComma: false }, 161 | initializer: 162 | { type: 'Identifier', 163 | start: 17, 164 | end: 18, 165 | value: 'x', 166 | context: 'variable' } } ] } ] }, 167 | 168 | }) 169 | -------------------------------------------------------------------------------- /test/parse/statements/for-semi.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** for (;;); **/ 4 | 'empty for-;': 5 | { type: 'Script', 6 | start: 0, 7 | end: 9, 8 | statements: 9 | [ { type: 'ForStatement', 10 | start: 0, 11 | end: 9, 12 | initializer: null, 13 | test: null, 14 | update: null, 15 | body: { type: 'EmptyStatement', start: 8, end: 9 } } ] }, 16 | 17 | /** for (var x;;); **/ 18 | 'for with var initializer': 19 | { type: 'Script', 20 | start: 0, 21 | end: 14, 22 | statements: 23 | [ { type: 'ForStatement', 24 | start: 0, 25 | end: 14, 26 | initializer: 27 | { type: 'VariableDeclaration', 28 | start: 5, 29 | end: 10, 30 | kind: 'var', 31 | declarations: 32 | [ { type: 'VariableDeclarator', 33 | start: 9, 34 | end: 10, 35 | pattern: 36 | { type: 'Identifier', 37 | start: 9, 38 | end: 10, 39 | value: 'x', 40 | context: 'declaration' }, 41 | initializer: null } ] }, 42 | test: null, 43 | update: null, 44 | body: { type: 'EmptyStatement', start: 13, end: 14 } } ] }, 45 | 46 | /** for (let x;;); **/ 47 | 'for with let declaration': 48 | { type: 'Script', 49 | start: 0, 50 | end: 14, 51 | statements: 52 | [ { type: 'ForStatement', 53 | start: 0, 54 | end: 14, 55 | initializer: 56 | { type: 'VariableDeclaration', 57 | start: 5, 58 | end: 10, 59 | kind: 'let', 60 | declarations: 61 | [ { type: 'VariableDeclarator', 62 | start: 9, 63 | end: 10, 64 | pattern: 65 | { type: 'Identifier', 66 | start: 9, 67 | end: 10, 68 | value: 'x', 69 | context: 'declaration' }, 70 | initializer: null } ] }, 71 | test: null, 72 | update: null, 73 | body: { type: 'EmptyStatement', start: 13, end: 14 } } ] }, 74 | 75 | /** for (const x = 1;;); **/ 76 | 'for with const declaration and initializer': 77 | { type: 'Script', 78 | start: 0, 79 | end: 20, 80 | statements: 81 | [ { type: 'ForStatement', 82 | start: 0, 83 | end: 20, 84 | initializer: 85 | { type: 'VariableDeclaration', 86 | start: 5, 87 | end: 16, 88 | kind: 'const', 89 | declarations: 90 | [ { type: 'VariableDeclarator', 91 | start: 11, 92 | end: 16, 93 | pattern: 94 | { type: 'Identifier', 95 | start: 11, 96 | end: 12, 97 | value: 'x', 98 | context: 'declaration' }, 99 | initializer: { type: 'NumberLiteral', start: 15, end: 16, value: 1 } } ] }, 100 | test: null, 101 | update: null, 102 | body: { type: 'EmptyStatement', start: 19, end: 20 } } ] }, 103 | 104 | /** for (const x;;); **/ 105 | 'for with const declaration and no initializer': {}, 106 | 107 | /** for (let [x, y] = [];;); **/ 108 | 'for with let declaration pattern': 109 | { type: 'Script', 110 | start: 0, 111 | end: 24, 112 | statements: 113 | [ { type: 'ForStatement', 114 | start: 0, 115 | end: 24, 116 | initializer: 117 | { type: 'VariableDeclaration', 118 | start: 5, 119 | end: 20, 120 | kind: 'let', 121 | declarations: 122 | [ { type: 'VariableDeclarator', 123 | start: 9, 124 | end: 20, 125 | pattern: 126 | { type: 'ArrayPattern', 127 | start: 9, 128 | end: 15, 129 | elements: 130 | [ { type: 'PatternElement', 131 | start: 10, 132 | end: 11, 133 | pattern: 134 | { type: 'Identifier', 135 | start: 10, 136 | end: 11, 137 | value: 'x', 138 | context: 'declaration' }, 139 | initializer: null }, 140 | { type: 'PatternElement', 141 | start: 13, 142 | end: 14, 143 | pattern: 144 | { type: 'Identifier', 145 | start: 13, 146 | end: 14, 147 | value: 'y', 148 | context: 'declaration' }, 149 | initializer: null } ], 150 | trailingComma: false }, 151 | initializer: 152 | { type: 'ArrayLiteral', 153 | start: 18, 154 | end: 20, 155 | elements: [], 156 | trailingComma: false } } ] }, 157 | test: null, 158 | update: null, 159 | body: { type: 'EmptyStatement', start: 23, end: 24 } } ] }, 160 | 161 | /** for (let [x, y];;); **/ 162 | 'for with let declaration pattern and no initializer': {}, 163 | 164 | }) 165 | -------------------------------------------------------------------------------- /test/parse/functions/async-generators.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** async function *f() {} **/ 4 | 'async generator declaration': 5 | { type: 'Script', 6 | start: 0, 7 | end: 22, 8 | statements: 9 | [ { type: 'FunctionDeclaration', 10 | start: 0, 11 | end: 22, 12 | kind: 'async-generator', 13 | identifier: 14 | { type: 'Identifier', 15 | start: 16, 16 | end: 17, 17 | value: 'f', 18 | context: 'declaration' }, 19 | params: [], 20 | body: { type: 'FunctionBody', start: 20, end: 22, statements: [] } } ] }, 21 | 22 | /** (async function *f() {}); **/ 23 | 'async generator expression': 24 | { type: 'Script', 25 | start: 0, 26 | end: 25, 27 | statements: 28 | [ { type: 'ExpressionStatement', 29 | start: 0, 30 | end: 25, 31 | expression: 32 | { type: 'ParenExpression', 33 | start: 0, 34 | end: 24, 35 | expression: 36 | { type: 'FunctionExpression', 37 | start: 1, 38 | end: 23, 39 | kind: 'async-generator', 40 | identifier: 41 | { type: 'Identifier', 42 | start: 17, 43 | end: 18, 44 | value: 'f', 45 | context: 'declaration' }, 46 | params: [], 47 | body: { type: 'FunctionBody', start: 21, end: 23, statements: [] } } } } ] }, 48 | 49 | /** class C { async *ag() { await x; yield y; } } **/ 50 | 'async generator methods': 51 | { type: 'Script', 52 | start: 0, 53 | end: 45, 54 | statements: 55 | [ { type: 'ClassDeclaration', 56 | start: 0, 57 | end: 45, 58 | identifier: 59 | { type: 'Identifier', 60 | start: 6, 61 | end: 7, 62 | value: 'C', 63 | context: 'declaration' }, 64 | base: null, 65 | body: 66 | { type: 'ClassBody', 67 | start: 8, 68 | end: 45, 69 | elements: 70 | [ { type: 'MethodDefinition', 71 | start: 10, 72 | end: 43, 73 | static: false, 74 | kind: 'async-generator', 75 | name: 76 | { type: 'Identifier', 77 | start: 17, 78 | end: 19, 79 | value: 'ag', 80 | context: '' }, 81 | params: [], 82 | body: 83 | { type: 'FunctionBody', 84 | start: 22, 85 | end: 43, 86 | statements: 87 | [ { type: 'ExpressionStatement', 88 | start: 24, 89 | end: 32, 90 | expression: 91 | { type: 'UnaryExpression', 92 | start: 24, 93 | end: 31, 94 | operator: 'await', 95 | expression: 96 | { type: 'Identifier', 97 | start: 30, 98 | end: 31, 99 | value: 'x', 100 | context: 'variable' } } }, 101 | { type: 'ExpressionStatement', 102 | start: 33, 103 | end: 41, 104 | expression: 105 | { type: 'YieldExpression', 106 | start: 33, 107 | end: 40, 108 | delegate: false, 109 | expression: 110 | { type: 'Identifier', 111 | start: 39, 112 | end: 40, 113 | value: 'y', 114 | context: 'variable' } } } ] } } ] } } ] }, 115 | 116 | /** async function *f() { await x; yield y; } **/ 117 | 'await and yield are allowed in async generators': 118 | { type: 'Script', 119 | start: 0, 120 | end: 41, 121 | statements: 122 | [ { type: 'FunctionDeclaration', 123 | start: 0, 124 | end: 41, 125 | kind: 'async-generator', 126 | identifier: 127 | { type: 'Identifier', 128 | start: 16, 129 | end: 17, 130 | value: 'f', 131 | context: 'declaration' }, 132 | params: [], 133 | body: 134 | { type: 'FunctionBody', 135 | start: 20, 136 | end: 41, 137 | statements: 138 | [ { type: 'ExpressionStatement', 139 | start: 22, 140 | end: 30, 141 | expression: 142 | { type: 'UnaryExpression', 143 | start: 22, 144 | end: 29, 145 | operator: 'await', 146 | expression: 147 | { type: 'Identifier', 148 | start: 28, 149 | end: 29, 150 | value: 'x', 151 | context: 'variable' } } }, 152 | { type: 'ExpressionStatement', 153 | start: 31, 154 | end: 39, 155 | expression: 156 | { type: 'YieldExpression', 157 | start: 31, 158 | end: 38, 159 | delegate: false, 160 | expression: 161 | { type: 'Identifier', 162 | start: 37, 163 | end: 38, 164 | value: 'y', 165 | context: 'variable' } } } ] } } ] }, 166 | 167 | }) 168 | -------------------------------------------------------------------------------- /test/parse/statements/labelled.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** label: x; label: x; **/ 4 | 'duplicate labels are allowed': 5 | { type: 'Script', 6 | start: 0, 7 | end: 19, 8 | statements: 9 | [ { type: 'LabelledStatement', 10 | start: 0, 11 | end: 9, 12 | label: 13 | { type: 'Identifier', 14 | start: 0, 15 | end: 5, 16 | value: 'label', 17 | context: '' }, 18 | statement: 19 | { type: 'ExpressionStatement', 20 | start: 7, 21 | end: 9, 22 | expression: 23 | { type: 'Identifier', 24 | start: 7, 25 | end: 8, 26 | value: 'x', 27 | context: 'variable' } } }, 28 | { type: 'LabelledStatement', 29 | start: 10, 30 | end: 19, 31 | label: 32 | { type: 'Identifier', 33 | start: 10, 34 | end: 15, 35 | value: 'label', 36 | context: '' }, 37 | statement: 38 | { type: 'ExpressionStatement', 39 | start: 17, 40 | end: 19, 41 | expression: 42 | { type: 'Identifier', 43 | start: 17, 44 | end: 18, 45 | value: 'x', 46 | context: 'variable' } } } ] }, 47 | 48 | /** label: { break label; } **/ 49 | 'break allowed outside of iteration or switch': 50 | { type: 'Script', 51 | start: 0, 52 | end: 23, 53 | statements: 54 | [ { type: 'LabelledStatement', 55 | start: 0, 56 | end: 23, 57 | label: 58 | { type: 'Identifier', 59 | start: 0, 60 | end: 5, 61 | value: 'label', 62 | context: '' }, 63 | statement: 64 | { type: 'Block', 65 | start: 7, 66 | end: 23, 67 | statements: 68 | [ { type: 'BreakStatement', 69 | start: 9, 70 | end: 21, 71 | label: 72 | { type: 'Identifier', 73 | start: 15, 74 | end: 20, 75 | value: 'label', 76 | context: '' } } ] } } ] }, 77 | 78 | /** switch (x) { default: break; } **/ 79 | 'unlabelled break within switch': 80 | { type: 'Script', 81 | start: 0, 82 | end: 30, 83 | statements: 84 | [ { type: 'SwitchStatement', 85 | start: 0, 86 | end: 30, 87 | descriminant: 88 | { type: 'Identifier', 89 | start: 8, 90 | end: 9, 91 | value: 'x', 92 | context: 'variable' }, 93 | cases: 94 | [ { type: 'SwitchCase', 95 | start: 13, 96 | end: 28, 97 | test: null, 98 | consequent: [ { type: 'BreakStatement', start: 22, end: 28, label: null } ] } ] } ] }, 99 | 100 | /** while (true) { break } **/ 101 | 'unlabelled break within iteration statement': 102 | { type: 'Script', 103 | start: 0, 104 | end: 22, 105 | statements: 106 | [ { type: 'WhileStatement', 107 | start: 0, 108 | end: 22, 109 | test: { type: 'BooleanLiteral', start: 7, end: 11, value: true }, 110 | body: 111 | { type: 'Block', 112 | start: 13, 113 | end: 22, 114 | statements: [ { type: 'BreakStatement', start: 15, end: 20, label: null } ] } } ] }, 115 | 116 | /** while (true) { continue } **/ 117 | 'unlabelled continue inside of iteration statement': 118 | { type: 'Script', 119 | start: 0, 120 | end: 25, 121 | statements: 122 | [ { type: 'WhileStatement', 123 | start: 0, 124 | end: 25, 125 | test: { type: 'BooleanLiteral', start: 7, end: 11, value: true }, 126 | body: 127 | { type: 'Block', 128 | start: 13, 129 | end: 25, 130 | statements: [ { type: 'ContinueStatement', start: 15, end: 23, label: null } ] } } ] }, 131 | 132 | /** label: function x() {} **/ 133 | 'function declarations are allowed within labelled statements in sloppy mode (Annex B)': 134 | { type: 'Script', 135 | start: 0, 136 | end: 22, 137 | statements: 138 | [ { type: 'LabelledStatement', 139 | start: 0, 140 | end: 22, 141 | label: 142 | { type: 'Identifier', 143 | start: 0, 144 | end: 5, 145 | value: 'label', 146 | context: '' }, 147 | statement: 148 | { type: 'FunctionDeclaration', 149 | start: 7, 150 | end: 22, 151 | kind: '', 152 | identifier: 153 | { type: 'Identifier', 154 | start: 16, 155 | end: 17, 156 | value: 'x', 157 | context: 'declaration' }, 158 | params: [], 159 | body: { type: 'FunctionBody', start: 20, end: 22, statements: [] }, 160 | error: 'Labeled FunctionDeclarations are disallowed in strict mode' } } ] }, 161 | 162 | /** "use strict"; label: function x() {} **/ 163 | 'labelled function declarations are not allowed in strict mode': {}, 164 | 165 | /** label: { break lbl; } **/ 166 | 'labelled break must refer to a named label': {}, 167 | 168 | /** label: { break; } **/ 169 | 'unlabelled break outside of iteration or switch not allowed - 1': {}, 170 | 171 | /** break **/ 172 | 'unlabelled break outside of iteration or switch not allowed - 2': {}, 173 | 174 | /** label: { label: x; } **/ 175 | 'duplicate nested labels are not allowed': {}, 176 | 177 | /** label: { for (; true;) continue label; } **/ 178 | 'continue statement with label must reference an iteration statement': {}, 179 | 180 | /** continue; **/ 181 | 'continue not allowed outside of iteration statement - 1': {}, 182 | 183 | /** switch (x) { default: continue; } **/ 184 | 'continue not allowed outside of iteration statement - 2': {} 185 | 186 | }) 187 | -------------------------------------------------------------------------------- /src/Transform.js: -------------------------------------------------------------------------------- 1 | import * as AST from './AST.js'; 2 | import { isReservedWord } from './Scanner.js'; 3 | 4 | export class Transform { 5 | 6 | // Transform an expression into a formal parameter list 7 | transformFormals(expr) { 8 | if (!expr) 9 | return []; 10 | 11 | let trailingComma = false; 12 | let list; 13 | 14 | switch (expr.type) { 15 | case 'SequenceExpression': 16 | list = expr.expressions; 17 | if (expr.error) { 18 | trailingComma = true; 19 | expr.error = ''; 20 | } 21 | break; 22 | case 'CallExpression': 23 | list = expr.arguments; 24 | trailingComma = expr.trailingComma; 25 | break; 26 | default: 27 | list = [expr]; 28 | break; 29 | } 30 | 31 | for (let i = 0; i < list.length; ++i) { 32 | let node = list[i]; 33 | let param; 34 | 35 | if (i === list.length - 1 && node.type === 'SpreadExpression') { 36 | expr = node.expression; 37 | 38 | // Trailing commas not allowed after rest parameters 39 | if (trailingComma) 40 | this.fail('Trailing comma not allowed after rest parameter', expr); 41 | 42 | // Rest parameters can only be identifiers 43 | if (expr.type !== 'Identifier') 44 | this.fail('Invalid rest parameter', expr); 45 | 46 | this.checkBindingTarget(expr); 47 | 48 | // Clear parser error for invalid spread expression 49 | node.error = ''; 50 | 51 | param = this.node(new AST.RestParameter(expr), node.start, node.end); 52 | 53 | } else { 54 | 55 | param = this.node(new AST.FormalParameter(node, null), node.start, node.end); 56 | this.transformPatternElement(param, true); 57 | } 58 | 59 | list[i] = param; 60 | } 61 | 62 | return list; 63 | } 64 | 65 | transformArrayPattern(node, binding) { 66 | node.type = 'ArrayPattern'; // ArrayPattern and ArrayLiteral are isomorphic 67 | 68 | let elems = node.elements; 69 | 70 | for (let i = 0; i < elems.length; ++i) { 71 | let elem = elems[i]; 72 | 73 | // Skip holes in pattern 74 | if (!elem) 75 | continue; 76 | 77 | switch (elem.type) { 78 | case 'SpreadExpression': 79 | // Rest element must be in the last position and cannot be followed by a comma 80 | if (i < elems.length - 1 || node.trailingComma) 81 | this.fail('Invalid destructuring pattern', elem); 82 | 83 | elem = this.node(new AST.PatternRestElement(elem.expression), elem.start, elem.end); 84 | this.checkPatternTarget(elem.pattern, binding); 85 | break; 86 | 87 | case 'PatternRestElement': 88 | this.checkPatternTarget(elem.pattern, binding); 89 | break; 90 | 91 | case 'PatternElement': 92 | this.transformPatternElement(elem, binding); 93 | break; 94 | 95 | default: 96 | elem = this.node(new AST.PatternElement(elem, null), elem.start, elem.end); 97 | this.transformPatternElement(elem, binding); 98 | break; 99 | } 100 | 101 | elems[i] = elem; 102 | } 103 | 104 | } 105 | 106 | transformObjectPattern(node, binding) { 107 | node.type = 'ObjectPattern'; // ObjectPattern and ObjectLiteral are isomorphic 108 | 109 | let props = node.properties; 110 | 111 | for (let i = 0; i < props.length; ++i) { 112 | let prop = props[i]; 113 | 114 | // Clear the error flag 115 | prop.error = ''; 116 | 117 | switch (prop.type) { 118 | case 'PropertyDefinition': 119 | prop = this.node( 120 | new AST.PatternProperty(prop.name, prop.expression, null), 121 | prop.start, 122 | prop.end 123 | ); 124 | break; 125 | 126 | case 'SpreadExpression': 127 | // Rest element must be in the last position and cannot be followed by a comma 128 | if (i < props.length - 1 || node.trailingComma) 129 | this.fail('Invalid destructuring pattern', prop); 130 | 131 | // Rest target cannot be a destructuring pattern 132 | switch (prop.expression.type) { 133 | case 'ObjectLiteral': 134 | case 'ObjectPattern': 135 | case 'ArrayLiteral': 136 | case 'ArrayPattern': 137 | this.fail('Invalid rest pattern', prop.expression); 138 | } 139 | 140 | prop = this.node(new AST.PatternRestElement(prop.expression), prop.start, prop.end); 141 | break; 142 | 143 | case 'PatternProperty': 144 | break; 145 | 146 | default: 147 | this.fail('Invalid pattern', prop); 148 | } 149 | 150 | props[i] = prop; 151 | 152 | if (prop.pattern) 153 | this.transformPatternElement(prop, binding); 154 | else 155 | this.checkPatternTarget(prop.name, binding); 156 | } 157 | } 158 | 159 | transformPatternElement(elem, binding) { 160 | let node = elem.pattern; 161 | 162 | // Split assignment into pattern and initializer 163 | if (node && node.type === 'AssignmentExpression' && node.operator === '=') { 164 | elem.initializer = node.right; 165 | elem.pattern = node = node.left; 166 | } 167 | 168 | this.checkPatternTarget(node, binding); 169 | } 170 | 171 | transformIdentifier(node) { 172 | let value = node.value; 173 | 174 | if (isReservedWord(value)) 175 | this.fail('Unexpected token ' + value, node); 176 | 177 | this.checkIdentifier(node); 178 | node.context = 'variable'; 179 | } 180 | 181 | transformDefaultExport(node) { 182 | switch (node.type) { 183 | case 'ClassExpression': 184 | node.type = 'ClassDeclaration'; 185 | return true; 186 | 187 | case 'FunctionExpression': 188 | node.type = 'FunctionDeclaration'; 189 | return true; 190 | } 191 | 192 | return false; 193 | } 194 | 195 | } 196 | -------------------------------------------------------------------------------- /test/parse/functions/get-set.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** ({ get x() {} }); **/ 4 | 'object literal with getter': 5 | { type: 'Script', 6 | start: 0, 7 | end: 17, 8 | statements: 9 | [ { type: 'ExpressionStatement', 10 | start: 0, 11 | end: 17, 12 | expression: 13 | { type: 'ParenExpression', 14 | start: 0, 15 | end: 16, 16 | expression: 17 | { type: 'ObjectLiteral', 18 | start: 1, 19 | end: 15, 20 | properties: 21 | [ { type: 'MethodDefinition', 22 | start: 3, 23 | end: 13, 24 | static: false, 25 | kind: 'get', 26 | name: { type: 'Identifier', start: 7, end: 8, value: 'x', context: '' }, 27 | params: [], 28 | body: { type: 'FunctionBody', start: 11, end: 13, statements: [] } } ], 29 | trailingComma: false } } } ] }, 30 | 31 | /** ({ set x(val) {} }); **/ 32 | 'object literal with setter': 33 | { type: 'Script', 34 | start: 0, 35 | end: 20, 36 | statements: 37 | [ { type: 'ExpressionStatement', 38 | start: 0, 39 | end: 20, 40 | expression: 41 | { type: 'ParenExpression', 42 | start: 0, 43 | end: 19, 44 | expression: 45 | { type: 'ObjectLiteral', 46 | start: 1, 47 | end: 18, 48 | properties: 49 | [ { type: 'MethodDefinition', 50 | start: 3, 51 | end: 16, 52 | static: false, 53 | kind: 'set', 54 | name: { type: 'Identifier', start: 7, end: 8, value: 'x', context: '' }, 55 | params: 56 | [ { type: 'FormalParameter', 57 | start: 9, 58 | end: 12, 59 | pattern: 60 | { type: 'Identifier', 61 | start: 9, 62 | end: 12, 63 | value: 'val', 64 | context: 'declaration' }, 65 | initializer: null } ], 66 | body: { type: 'FunctionBody', start: 14, end: 16, statements: [] } } ], 67 | trailingComma: false } } } ] }, 68 | 69 | /** ({ get: 0, set: 0 }); **/ 70 | 'get and set are valid property names': 71 | { type: 'Script', 72 | start: 0, 73 | end: 21, 74 | statements: 75 | [ { type: 'ExpressionStatement', 76 | start: 0, 77 | end: 21, 78 | expression: 79 | { type: 'ParenExpression', 80 | start: 0, 81 | end: 20, 82 | expression: 83 | { type: 'ObjectLiteral', 84 | start: 1, 85 | end: 19, 86 | properties: 87 | [ { type: 'PropertyDefinition', 88 | start: 3, 89 | end: 9, 90 | name: { type: 'Identifier', start: 3, end: 6, value: 'get', context: '' }, 91 | expression: { type: 'NumberLiteral', start: 8, end: 9, value: 0 } }, 92 | { type: 'PropertyDefinition', 93 | start: 11, 94 | end: 17, 95 | name: 96 | { type: 'Identifier', 97 | start: 11, 98 | end: 14, 99 | value: 'set', 100 | context: '' }, 101 | expression: { type: 'NumberLiteral', start: 16, end: 17, value: 0 } } ], 102 | trailingComma: false } } } ] }, 103 | 104 | /** ({ set x({ y }) {} }); **/ 105 | 'setter can have a destructuring param': 106 | { type: 'Script', 107 | start: 0, 108 | end: 22, 109 | statements: 110 | [ { type: 'ExpressionStatement', 111 | start: 0, 112 | end: 22, 113 | expression: 114 | { type: 'ParenExpression', 115 | start: 0, 116 | end: 21, 117 | expression: 118 | { type: 'ObjectLiteral', 119 | start: 1, 120 | end: 20, 121 | properties: 122 | [ { type: 'MethodDefinition', 123 | start: 3, 124 | end: 18, 125 | static: false, 126 | kind: 'set', 127 | name: { type: 'Identifier', start: 7, end: 8, value: 'x', context: '' }, 128 | params: 129 | [ { type: 'FormalParameter', 130 | start: 9, 131 | end: 14, 132 | pattern: 133 | { type: 'ObjectPattern', 134 | start: 9, 135 | end: 14, 136 | properties: 137 | [ { type: 'PatternProperty', 138 | start: 11, 139 | end: 12, 140 | name: 141 | { type: 'Identifier', 142 | start: 11, 143 | end: 12, 144 | value: 'y', 145 | context: 'declaration' }, 146 | pattern: null, 147 | initializer: null } ], 148 | trailingComma: false }, 149 | initializer: null } ], 150 | body: { type: 'FunctionBody', start: 16, end: 18, statements: [] } } ], 151 | trailingComma: false } } } ] }, 152 | 153 | /** ({ get x(val) {} }); **/ 154 | 'getter cannot have any parameters': {}, 155 | 156 | /** ({ set x() {} }); **/ 157 | 'setter must have a single parameter - 1': {}, 158 | 159 | /** ({ set x(a, b) {} }); **/ 160 | 'setter must have a single parameter - 2': {}, 161 | 162 | /** ({ set x(...args) {} }); **/ 163 | 'setter cannot have a rest parameter': {}, 164 | 165 | /** ({ set x(val = 0) {} }); **/ 166 | 'setter cannot have a parameter default': {}, 167 | 168 | }); 169 | -------------------------------------------------------------------------------- /test/runner.js: -------------------------------------------------------------------------------- 1 | const Path = require('path'); 2 | const FS = require('fs'); 3 | const { inspect } = require('util'); 4 | 5 | const TEST_COMMENT = /\/\*\*[\s\S]+?\*\*\//g; 6 | const COMMENT_TRIM = /^\/\*+\s+|\s+\*+\/$/g; 7 | const HOP = {}.hasOwnProperty; 8 | 9 | // Returns a stat object for a path 10 | function statPath(path) { 11 | try { 12 | return FS.statSync(path); 13 | } catch (ex) {} 14 | return null; 15 | } 16 | 17 | // Executes a function for each file in a directory 18 | function walkDirectory(dir, fn) { 19 | FS 20 | .readdirSync(dir) 21 | .filter(name => name.charAt(0) !== '.') 22 | .map(name => Path.resolve(dir, name)) 23 | .map(path => ({ 24 | path: path, 25 | stat: statPath(path) 26 | })) 27 | .forEach(entry => { 28 | if (!entry.stat) 29 | return; 30 | 31 | if (entry.stat.isDirectory()) 32 | return walkDirectory(entry.path, fn); 33 | 34 | if (entry.stat.isFile()) 35 | fn(entry.path); 36 | }); 37 | } 38 | 39 | let Style = new class { 40 | 41 | green(msg) { 42 | return `${ msg }`; 43 | } 44 | 45 | red(msg) { 46 | return `${ msg }`; 47 | } 48 | 49 | gray(msg) { 50 | return `${ msg }`; 51 | } 52 | 53 | bold(msg) { 54 | return `${ msg }`; 55 | } 56 | 57 | }; 58 | 59 | // Prints an application message to the console 60 | function printMessage(msg) { 61 | console.log(Style.gray(msg)); 62 | } 63 | 64 | // Prints a group header to the console 65 | function printHeader(msg) { 66 | console.log(`\n${ Style.bold('== ' + msg + ' ==') }\n`); 67 | } 68 | 69 | // Read a javascript or json file 70 | function readFile(filename) { 71 | let text = FS.readFileSync(filename, 'utf8'); 72 | 73 | // From node/lib/module.js/Module.prototype._compile 74 | text = text.replace(/^\#\!.*/, ''); 75 | 76 | // From node/lib/module.js/stripBOM 77 | if (text.charCodeAt(0) === 0xFEFF) 78 | text = text.slice(1); 79 | 80 | return text; 81 | } 82 | 83 | // Parses a list of test inputs from comments 84 | function parseTestComments(text) { 85 | let list = text.match(TEST_COMMENT) || []; 86 | 87 | return list.map(source => { 88 | return { 89 | module: source.startsWith('/***'), 90 | source: source.replace(COMMENT_TRIM, ''), 91 | }; 92 | }); 93 | } 94 | 95 | // Returns true if the argument is an object 96 | function isObject(obj) { 97 | return obj && typeof obj === 'object'; 98 | } 99 | 100 | // Returns true if the specified object is 'like' another object 101 | function objectLike(a, b, skipKeys) { 102 | if (a === b) 103 | return true; 104 | 105 | if (!isObject(a) || !isObject(b)) 106 | return a === b; 107 | 108 | // Each key in control must be in test 109 | for (let keys = Object.keys(b), i = 0; i < keys.length; ++i) 110 | if (!HOP.call(a, keys[i])) 111 | return false; 112 | 113 | for (let keys = Object.keys(a), i = 0; i < keys.length; ++i) { 114 | // Control must have same own property 115 | if (!HOP.call(b, keys[i])) { 116 | if (skipKeys && skipKeys.indexOf(keys[i]) >= 0) continue; 117 | else return false; 118 | } 119 | 120 | // Values of own properties must be equal 121 | if (!objectLike(a[keys[i]], b[keys[i]], skipKeys)) 122 | return false; 123 | } 124 | 125 | return true; 126 | } 127 | 128 | function defaultRender(obj) { 129 | return inspect(obj, { depth: 20, colors: true }); 130 | } 131 | 132 | function runTests(options) { 133 | let testsPassed = 0; 134 | let testsFailed = 0; 135 | let dirname = options.dir; 136 | let process = options.process; 137 | let compare = options.compare; 138 | let render = options.render || defaultRender; 139 | 140 | function printResult(msg, pass) { 141 | console.log(msg + ' ' + (pass ? Style.green('OK') : Style.bold(Style.red('FAIL')))); 142 | if (pass) testsPassed++; 143 | else testsFailed++; 144 | } 145 | 146 | // Returns the group name for a test file 147 | function groupName(path) { 148 | path = Path.dirname(path); 149 | 150 | if (path.indexOf(__dirname) === 0) 151 | path = path.slice(dirname.length); 152 | 153 | return path.replace(/[\/\\]/g, '.').replace(/^\./, ''); 154 | } 155 | 156 | function run() { 157 | let currentGroup = null; 158 | 159 | printMessage('\nStarting tests...'); 160 | 161 | walkDirectory(dirname, path => { 162 | let group = groupName(path); 163 | let name = Path.basename(path, '.js'); 164 | let tree; 165 | 166 | // Only javascript files in nested directories 167 | if (!group || Path.extname(path) !== '.js') 168 | return; 169 | 170 | // Print a group header 171 | if (group !== currentGroup) 172 | printHeader(currentGroup = group); 173 | 174 | let text = readFile(path); 175 | let programs = parseTestComments(text); 176 | let outputs = (new Function('return ' + text))(); 177 | let keys = Object.keys(outputs); 178 | 179 | for (let i = 0; i < programs.length; ++i) { 180 | let program = programs[i]; 181 | 182 | try { 183 | tree = process(program.source, { 184 | module: program.module 185 | }); 186 | } catch (err) { 187 | if (err instanceof SyntaxError) 188 | tree = { message: err.message }; 189 | else 190 | throw err; 191 | } 192 | 193 | let pass = compare(tree, outputs[keys[i]]); 194 | printResult(name + ' - ' + keys[i], pass); 195 | 196 | if (!pass) { 197 | printMessage('\nGenerated tree:\n'); 198 | console.log(render(tree)); 199 | throw 'stop'; 200 | } 201 | } 202 | }); 203 | } 204 | 205 | try { 206 | run(); 207 | printMessage('\nSuccessfully completed ' + testsPassed + ' tests - looks good to me!'); 208 | } catch (err) { 209 | if (err === 'stop') { 210 | console.log(' '); 211 | global.process.exit(1); 212 | return; 213 | } 214 | printMessage('\nSnap! An error has occurred.'); 215 | throw err; 216 | } finally { 217 | console.log(''); 218 | } 219 | } 220 | 221 | exports.objectLike = objectLike; 222 | exports.runTests = runTests; 223 | -------------------------------------------------------------------------------- /src/Validate.js: -------------------------------------------------------------------------------- 1 | import { isStrictReservedWord } from './Scanner.js'; 2 | 3 | // Returns true if the specified name is a restricted identifier in strict mode 4 | function isPoisonIdent(name) { 5 | return name === 'eval' || name === 'arguments'; 6 | } 7 | 8 | export class Validate { 9 | 10 | // Validates an assignment target 11 | checkAssignmentTarget(node, simple) { 12 | if (!simple && node.type === 'ParenExpression') { 13 | node = this.unwrapParens(node); 14 | simple = true; 15 | } 16 | 17 | switch (node.type) { 18 | case 'Identifier': 19 | if (isPoisonIdent(node.value)) 20 | this.addStrictError('Cannot modify ' + node.value + ' in strict mode', node); 21 | 22 | return; 23 | 24 | case 'MemberExpression': 25 | return; 26 | 27 | case 'ObjectPattern': 28 | case 'ArrayPattern': 29 | if (!simple) return; 30 | break; 31 | 32 | case 'ObjectLiteral': 33 | if (!simple) { 34 | this.transformObjectPattern(node, false); 35 | return; 36 | } 37 | break; 38 | 39 | case 'ArrayLiteral': 40 | if (!simple) { 41 | this.transformArrayPattern(node, false); 42 | return; 43 | } 44 | break; 45 | } 46 | 47 | this.fail('Invalid left-hand side in assignment', node); 48 | } 49 | 50 | // Validates a binding target 51 | checkBindingTarget(node) { 52 | switch (node.type) { 53 | case 'Identifier': { 54 | // Perform basic identifier validation 55 | this.checkIdentifier(node); 56 | 57 | // Mark identifier node as a declaration 58 | node.context = 'declaration'; 59 | 60 | let name = node.value; 61 | 62 | if (isPoisonIdent(name)) 63 | this.addStrictError(`Binding cannot be created for '${ name }' in strict mode`, node); 64 | 65 | return; 66 | } 67 | 68 | case 'ArrayLiteral': 69 | case 'ArrayPattern': 70 | this.transformArrayPattern(node, true); 71 | return; 72 | 73 | case 'ObjectLiteral': 74 | case 'ObjectPattern': 75 | this.transformObjectPattern(node, true); 76 | return; 77 | } 78 | 79 | this.fail('Invalid binding target', node); 80 | } 81 | 82 | // Validates a target in a binding or assignment pattern 83 | checkPatternTarget(node, binding) { 84 | return binding ? this.checkBindingTarget(node) : this.checkAssignmentTarget(node, false); 85 | } 86 | 87 | // Checks an identifier for strict mode reserved words 88 | checkIdentifier(node) { 89 | let ident = node.value; 90 | 91 | if (ident === 'yield' && this.context.isGenerator) 92 | this.fail('yield cannot be an identifier inside of a generator function', node); 93 | else if (ident === 'await' && this.context.isAsync) 94 | this.fail('await cannot be an identifier inside of an async function', node); 95 | else if (isStrictReservedWord(ident)) 96 | this.addStrictError(ident + ' cannot be used as an identifier in strict mode', node); 97 | } 98 | 99 | // Checks function formal parameters for strict mode restrictions 100 | checkParameters(params) { 101 | for (let i = 0; i < params.length; ++i) { 102 | let node = params[i]; 103 | 104 | if (node.type !== 'FormalParameter' || node.pattern.type !== 'Identifier') { 105 | this.context.allowUseStrict = false; 106 | continue; 107 | } 108 | 109 | if (node.initializer) 110 | this.context.allowUseStrict = false; 111 | 112 | let name = node.pattern.value; 113 | if (isPoisonIdent(name)) 114 | this.addStrictError('Parameter name ' + name + ' is not allowed in strict mode', node); 115 | } 116 | } 117 | 118 | // Performs validation on transformed arrow formal parameters 119 | checkArrowParameters(params) { 120 | params = this.transformFormals(params); 121 | this.checkParameters(params); 122 | return params; 123 | } 124 | 125 | // Performs validation on the init portion of a for-in or for-of statement 126 | checkForInit(init, iterationType) { 127 | if (!init) 128 | return; 129 | 130 | if (!iterationType) { 131 | if (init.type !== 'VariableDeclaration') 132 | return; 133 | 134 | init.declarations.forEach(decl => { 135 | if (decl.initializer) 136 | return; 137 | 138 | // Enforce const initialization in for(;;) 139 | if (init.kind === 'const') 140 | this.fail('Missing const initializer', decl.pattern); 141 | 142 | // Enforce pattern initialization in for(;;) 143 | if (decl.pattern.type !== 'Identifier') 144 | this.fail('Missing pattern initializer', decl.pattern); 145 | }); 146 | 147 | return; 148 | } 149 | 150 | if (init.type === 'VariableDeclaration') { 151 | 152 | // For-in/of may only have one variable declaration 153 | if (init.declarations.length !== 1) { 154 | this.fail('for-' + iterationType + ' statement may not have more than ' + 155 | 'one variable declaration', init); 156 | } 157 | 158 | let decl = init.declarations[0]; 159 | 160 | // Initializers are not allowed in for in and for of 161 | if (decl.initializer) { 162 | let msg = 'Invalid initializer in for-' + iterationType + ' statement'; 163 | if (iterationType === 'in') this.addStrictError(msg, init); 164 | else this.fail(msg); 165 | } 166 | 167 | } else { 168 | 169 | this.checkAssignmentTarget(this.unwrapParens(init)); 170 | } 171 | } 172 | 173 | checkInvalidNodes() { 174 | let context = this.context; 175 | let list = context.invalidNodes; 176 | 177 | for (let i = 0; i < list.length; ++i) { 178 | let item = list[i]; 179 | let node = item.node; 180 | let error = node.error; 181 | 182 | // Skip if error has been resolved 183 | if (!error) 184 | continue; 185 | 186 | // Skip if this is a strict-mode-only error in sloppy mode 187 | if (item.strict && !context.strict) 188 | continue; 189 | 190 | this.fail(error, node); 191 | } 192 | 193 | } 194 | 195 | checkDelete(node) { 196 | node = this.unwrapParens(node); 197 | 198 | if (node.type === 'Identifier') 199 | this.addStrictError('Cannot delete unqualified property in strict mode', node); 200 | } 201 | 202 | } 203 | -------------------------------------------------------------------------------- /test/parse/expressions/numbers.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** 42 **/ 4 | 'integer literal': 5 | { type: "Script", 6 | statements: [ 7 | 8 | { type: "ExpressionStatement", 9 | expression: 10 | { type: "NumberLiteral", 11 | value: 42 12 | } 13 | }] 14 | }, 15 | 16 | /** 42.42 **/ 17 | 'decimal literal': 18 | { type: "Script", 19 | statements: [ 20 | 21 | { type: "ExpressionStatement", 22 | expression: 23 | { type: "NumberLiteral", 24 | value: 42.42 25 | } 26 | }] 27 | }, 28 | 29 | /** .42 **/ 30 | 'leading point': 31 | { type: "Script", 32 | statements: [ 33 | 34 | { type: "ExpressionStatement", 35 | expression: 36 | { type: "NumberLiteral", 37 | value: .42 38 | } 39 | }] 40 | }, 41 | 42 | /** 0.42 **/ 43 | 'zero followed by decimal point': 44 | { type: "Script", 45 | statements: [ 46 | 47 | { type: "ExpressionStatement", 48 | expression: 49 | { type: "NumberLiteral", 50 | value: 0.42 51 | } 52 | }] 53 | }, 54 | 55 | /** 42e10 **/ 56 | 'integer with exponent': 57 | { type: "Script", 58 | statements: [ 59 | 60 | { type: "ExpressionStatement", 61 | expression: 62 | { type: "NumberLiteral", 63 | value: 42e10 64 | } 65 | }] 66 | }, 67 | 68 | /** 42e+10 **/ 69 | 'integer with signed exponent': 70 | { type: "Script", 71 | statements: [ 72 | 73 | { type: "ExpressionStatement", 74 | expression: 75 | { type: "NumberLiteral", 76 | value: 42e+10 77 | } 78 | }] 79 | }, 80 | 81 | /** 42e-10 **/ 82 | 'integer with negative exponent': 83 | { type: "Script", 84 | statements: [ 85 | 86 | { type: "ExpressionStatement", 87 | expression: 88 | { type: "NumberLiteral", 89 | value: 42e-10 90 | } 91 | }] 92 | }, 93 | 94 | /** 42E10 **/ 95 | 'exponent with capital E': 96 | { type: "Script", 97 | statements: [ 98 | 99 | { type: "ExpressionStatement", 100 | expression: 101 | { type: "NumberLiteral", 102 | value: 42E10 103 | } 104 | }] 105 | }, 106 | 107 | /** 42.42e10 **/ 108 | 'decimal value with exponent': 109 | { type: "Script", 110 | statements: [ 111 | 112 | { type: "ExpressionStatement", 113 | expression: 114 | { type: "NumberLiteral", 115 | value: 42.42e10 116 | } 117 | }] 118 | }, 119 | 120 | /** 0x42; **/ 121 | 'hex': 122 | { type: "Script", 123 | statements: [ 124 | 125 | { type: "ExpressionStatement", 126 | expression: 127 | { type: "NumberLiteral", 128 | value: 0x42 129 | } 130 | }] 131 | }, 132 | 133 | /** 0X42; **/ 134 | 'hex with capital X': 135 | { type: "Script", 136 | statements: [ 137 | 138 | { type: "ExpressionStatement", 139 | expression: 140 | { type: "NumberLiteral", 141 | value: 0x42 142 | } 143 | }] 144 | }, 145 | 146 | /** 0b1010; **/ 147 | 'binary literal': 148 | { type: "Script", 149 | statements: [ 150 | 151 | { type: "ExpressionStatement", 152 | expression: 153 | { type: "NumberLiteral", 154 | value: 10 155 | } 156 | }] 157 | }, 158 | 159 | /** 0B1010; **/ 160 | 'binary literal with capital B': 161 | { type: "Script", 162 | statements: [ 163 | 164 | { type: "ExpressionStatement", 165 | expression: 166 | { type: "NumberLiteral", 167 | value: 10 168 | } 169 | }] 170 | }, 171 | 172 | /** 0o777; **/ 173 | 'ocatal literal': 174 | { type: "Script", 175 | statements: [ 176 | 177 | { type: "ExpressionStatement", 178 | expression: 179 | { type: "NumberLiteral", 180 | value: 511 181 | } 182 | }] 183 | }, 184 | 185 | /** 0O777; **/ 186 | 'octal literal with capital O': 187 | { type: "Script", 188 | statements: [ 189 | 190 | { type: "ExpressionStatement", 191 | expression: 192 | { type: "NumberLiteral", 193 | value: 511 194 | } 195 | }] 196 | }, 197 | 198 | /** 0999; **/ 199 | 'not a legacy octal': 200 | { type: "Script", 201 | statements: [ 202 | 203 | { type: "ExpressionStatement", 204 | expression: 205 | { type: "NumberLiteral", 206 | value: 0999 207 | } 208 | }] 209 | }, 210 | 211 | /** 0777; **/ 212 | 'legacy octal': 213 | { type: "Script", 214 | statements: [ 215 | 216 | { type: "ExpressionStatement", 217 | expression: 218 | { type: "NumberLiteral", 219 | value: 511 220 | } 221 | }] 222 | }, 223 | 224 | /** "use strict"; 0777; **/ 225 | 'legacy octals not allowed in strict mode': 226 | {}, 227 | 228 | /** 42f **/ 229 | 'identifier character not allowed afer number': 230 | {}, 231 | 232 | /** 42n **/ 233 | 'bigint integer literal': 234 | { 235 | type: 'Script', 236 | start: 0, 237 | end: 3, 238 | statements: 239 | [ { 240 | type: 'ExpressionStatement', 241 | start: 0, 242 | end: 3, 243 | expression: { type: 'NumberLiteral', suffix: 'n', start: 0, end: 3, value: 42 } } ] }, 244 | 245 | /** 0x8n **/ 246 | 'bigint hex literal': 247 | { 248 | type: 'Script', 249 | start: 0, 250 | end: 4, 251 | statements: 252 | [ { 253 | type: 'ExpressionStatement', 254 | start: 0, 255 | end: 4, 256 | expression: { type: 'NumberLiteral', suffix: 'n', start: 0, end: 4, value: 8 } } ] }, 257 | 258 | /** 0b10101n **/ 259 | 'bigint binary literal': 260 | { 261 | type: 'Script', 262 | start: 0, 263 | end: 8, 264 | statements: 265 | [ { 266 | type: 'ExpressionStatement', 267 | start: 0, 268 | end: 8, 269 | expression: { type: 'NumberLiteral', suffix: 'n', start: 0, end: 8, value: 21 } } ] }, 270 | 271 | /** 0o777n **/ 272 | 'bigint octal literal': 273 | { 274 | type: 'Script', 275 | start: 0, 276 | end: 6, 277 | statements: 278 | [ { 279 | type: 'ExpressionStatement', 280 | start: 0, 281 | end: 6, 282 | expression: { type: 'NumberLiteral', suffix: 'n', start: 0, end: 6, value: 511 } } ] }, 283 | 284 | /** 42N **/ 285 | 'no capital N for bigints': {}, 286 | 287 | }) 288 | -------------------------------------------------------------------------------- /test/parse/expressions/templates.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** `abcdefg`; **/ 4 | 'template with single literal': 5 | { type: 'Script', 6 | start: 0, 7 | end: 10, 8 | statements: 9 | [ { type: 'ExpressionStatement', 10 | start: 0, 11 | end: 10, 12 | expression: 13 | { type: 'TemplateExpression', 14 | start: 0, 15 | end: 9, 16 | parts: 17 | [ { type: 'TemplatePart', 18 | start: 0, 19 | end: 9, 20 | value: 'abcdefg', 21 | raw: 'abcdefg', 22 | templateEnd: true } ] } } ] }, 23 | 24 | /** `abc$efg`; **/ 25 | 'template with a standalone $': 26 | { type: 'Script', 27 | start: 0, 28 | end: 10, 29 | statements: 30 | [ { type: 'ExpressionStatement', 31 | start: 0, 32 | end: 10, 33 | expression: 34 | { type: 'TemplateExpression', 35 | start: 0, 36 | end: 9, 37 | parts: 38 | [ { type: 'TemplatePart', 39 | start: 0, 40 | end: 9, 41 | value: 'abc$efg', 42 | raw: 'abc$efg', 43 | templateEnd: true } ] } } ] }, 44 | 45 | /** `abc${ d }efg`; **/ 46 | 'template with a single substitution': 47 | { type: 'Script', 48 | start: 0, 49 | end: 15, 50 | statements: 51 | [ { type: 'ExpressionStatement', 52 | start: 0, 53 | end: 15, 54 | expression: 55 | { type: 'TemplateExpression', 56 | start: 0, 57 | end: 14, 58 | parts: 59 | [ { type: 'TemplatePart', 60 | start: 0, 61 | end: 6, 62 | value: 'abc', 63 | raw: 'abc', 64 | templateEnd: false }, 65 | { type: 'Identifier', 66 | start: 7, 67 | end: 8, 68 | value: 'd', 69 | context: 'variable' }, 70 | { type: 'TemplatePart', 71 | start: 9, 72 | end: 14, 73 | value: 'efg', 74 | raw: 'efg', 75 | templateEnd: true } ] } } ] }, 76 | 77 | /** `abc${ `d` }efg`; **/ 78 | 'template with a substitution containing a template': 79 | { type: 'Script', 80 | start: 0, 81 | end: 17, 82 | statements: 83 | [ { type: 'ExpressionStatement', 84 | start: 0, 85 | end: 17, 86 | expression: 87 | { type: 'TemplateExpression', 88 | start: 0, 89 | end: 16, 90 | parts: 91 | [ { type: 'TemplatePart', 92 | start: 0, 93 | end: 6, 94 | value: 'abc', 95 | raw: 'abc', 96 | templateEnd: false }, 97 | { type: 'TemplateExpression', 98 | start: 7, 99 | end: 10, 100 | parts: 101 | [ { type: 'TemplatePart', 102 | start: 7, 103 | end: 10, 104 | value: 'd', 105 | raw: 'd', 106 | templateEnd: true } ] }, 107 | { type: 'TemplatePart', 108 | start: 11, 109 | end: 16, 110 | value: 'efg', 111 | raw: 'efg', 112 | templateEnd: true } ] } } ] }, 113 | 114 | /** a.b`z`; **/ 115 | 'tagged template with member expression tag': 116 | { type: 'Script', 117 | start: 0, 118 | end: 7, 119 | statements: 120 | [ { type: 'ExpressionStatement', 121 | start: 0, 122 | end: 7, 123 | expression: 124 | { type: 'TaggedTemplateExpression', 125 | start: 0, 126 | end: 6, 127 | tag: 128 | { type: 'MemberExpression', 129 | start: 0, 130 | end: 3, 131 | object: 132 | { type: 'Identifier', 133 | start: 0, 134 | end: 1, 135 | value: 'a', 136 | context: 'variable' }, 137 | property: { type: 'Identifier', start: 2, end: 3, value: 'b', context: '' } }, 138 | template: 139 | { type: 'TemplateExpression', 140 | start: 3, 141 | end: 6, 142 | parts: 143 | [ { type: 'TemplatePart', 144 | start: 3, 145 | end: 6, 146 | value: 'z', 147 | raw: 'z', 148 | templateEnd: true } ] } } } ] }, 149 | 150 | /** `\n` **/ 151 | 'raw value does not process escapes': 152 | { type: 'Script', 153 | start: 0, 154 | end: 4, 155 | statements: 156 | [ { type: 'ExpressionStatement', 157 | start: 0, 158 | end: 4, 159 | expression: 160 | { type: 'TemplateExpression', 161 | start: 0, 162 | end: 4, 163 | parts: 164 | [ { type: 'TemplatePart', 165 | start: 0, 166 | end: 4, 167 | value: '\n', 168 | raw: '\\n', 169 | templateEnd: true } ] } } ] }, 170 | 171 | /** `` **/ 172 | 'empty template': 173 | { type: 'Script', 174 | start: 0, 175 | end: 2, 176 | statements: 177 | [ { type: 'ExpressionStatement', 178 | start: 0, 179 | end: 2, 180 | expression: 181 | { type: 'TemplateExpression', 182 | start: 0, 183 | end: 2, 184 | parts: 185 | [ { type: 'TemplatePart', 186 | start: 0, 187 | end: 2, 188 | value: '', 189 | raw: '', 190 | templateEnd: true } ] } } ] }, 191 | 192 | /** `${1}${1}` **/ 193 | 'template with empty literals': 194 | { type: 'Script', 195 | start: 0, 196 | end: 10, 197 | statements: 198 | [ { type: 'ExpressionStatement', 199 | start: 0, 200 | end: 10, 201 | expression: 202 | { type: 'TemplateExpression', 203 | start: 0, 204 | end: 10, 205 | parts: 206 | [ { type: 'TemplatePart', 207 | start: 0, 208 | end: 3, 209 | value: '', 210 | raw: '', 211 | templateEnd: false }, 212 | { type: 'NumberLiteral', start: 3, end: 4, value: 1 }, 213 | { type: 'TemplatePart', 214 | start: 4, 215 | end: 7, 216 | value: '', 217 | raw: '', 218 | templateEnd: false }, 219 | { type: 'NumberLiteral', start: 7, end: 8, value: 1 }, 220 | { type: 'TemplatePart', 221 | start: 8, 222 | end: 10, 223 | value: '', 224 | raw: '', 225 | templateEnd: true } ] } } ] }, 226 | 227 | /** `\ 228 | ` **/ 229 | 'template with line continuation': 230 | { type: 'Script', 231 | start: 0, 232 | end: 4, 233 | statements: 234 | [ { type: 'ExpressionStatement', 235 | start: 0, 236 | end: 4, 237 | expression: 238 | { type: 'TemplateExpression', 239 | start: 0, 240 | end: 4, 241 | parts: 242 | [ { type: 'TemplatePart', 243 | start: 0, 244 | end: 4, 245 | value: '\n', 246 | raw: '\\\n', 247 | templateEnd: true } ] } } ] }, 248 | 249 | }) 250 | -------------------------------------------------------------------------------- /test/parse/functions/arrows.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** () => x; **/ 4 | 'empty paren with expression': 5 | { type: 'Script', 6 | start: 0, 7 | end: 8, 8 | statements: 9 | [ { type: 'ExpressionStatement', 10 | start: 0, 11 | end: 8, 12 | expression: 13 | { type: 'ArrowFunction', 14 | start: 0, 15 | end: 7, 16 | kind: '', 17 | params: [], 18 | body: 19 | { type: 'Identifier', 20 | start: 6, 21 | end: 7, 22 | value: 'x', 23 | context: 'variable' } } } ] }, 24 | 25 | /** x => x; **/ 26 | 'identifier with expression': 27 | { type: 'Script', 28 | start: 0, 29 | end: 7, 30 | statements: 31 | [ { type: 'ExpressionStatement', 32 | start: 0, 33 | end: 7, 34 | expression: 35 | { type: 'ArrowFunction', 36 | start: 0, 37 | end: 6, 38 | kind: '', 39 | params: 40 | [ { type: 'FormalParameter', 41 | start: 0, 42 | end: 1, 43 | pattern: 44 | { type: 'Identifier', 45 | start: 0, 46 | end: 1, 47 | value: 'x', 48 | context: 'declaration' }, 49 | initializer: null } ], 50 | body: 51 | { type: 'Identifier', 52 | start: 5, 53 | end: 6, 54 | value: 'x', 55 | context: 'variable' } } } ] }, 56 | 57 | /** (x, y) => x + y; **/ 58 | 'paren with expression': 59 | { type: 'Script', 60 | start: 0, 61 | end: 16, 62 | statements: 63 | [ { type: 'ExpressionStatement', 64 | start: 0, 65 | end: 16, 66 | expression: 67 | { type: 'ArrowFunction', 68 | start: 0, 69 | end: 15, 70 | kind: '', 71 | params: 72 | [ { type: 'FormalParameter', 73 | start: 1, 74 | end: 2, 75 | pattern: 76 | { type: 'Identifier', 77 | start: 1, 78 | end: 2, 79 | value: 'x', 80 | context: 'declaration' }, 81 | initializer: null }, 82 | { type: 'FormalParameter', 83 | start: 4, 84 | end: 5, 85 | pattern: 86 | { type: 'Identifier', 87 | start: 4, 88 | end: 5, 89 | value: 'y', 90 | context: 'declaration' }, 91 | initializer: null } ], 92 | body: 93 | { type: 'BinaryExpression', 94 | start: 10, 95 | end: 15, 96 | operator: '+', 97 | left: 98 | { type: 'Identifier', 99 | start: 10, 100 | end: 11, 101 | value: 'x', 102 | context: 'variable' }, 103 | right: 104 | { type: 'Identifier', 105 | start: 14, 106 | end: 15, 107 | value: 'y', 108 | context: 'variable' } } } } ] }, 109 | 110 | /** x => { return x; } **/ 111 | 'identifier with function body': 112 | { type: 'Script', 113 | start: 0, 114 | end: 18, 115 | statements: 116 | [ { type: 'ExpressionStatement', 117 | start: 0, 118 | end: 18, 119 | expression: 120 | { type: 'ArrowFunction', 121 | start: 0, 122 | end: 18, 123 | kind: '', 124 | params: 125 | [ { type: 'FormalParameter', 126 | start: 0, 127 | end: 1, 128 | pattern: 129 | { type: 'Identifier', 130 | start: 0, 131 | end: 1, 132 | value: 'x', 133 | context: 'declaration' }, 134 | initializer: null } ], 135 | body: 136 | { type: 'FunctionBody', 137 | start: 5, 138 | end: 18, 139 | statements: 140 | [ { type: 'ReturnStatement', 141 | start: 7, 142 | end: 16, 143 | argument: 144 | { type: 'Identifier', 145 | start: 14, 146 | end: 15, 147 | value: 'x', 148 | context: 'variable' } } ] } } } ] }, 149 | 150 | /** (...args) => { } **/ 151 | 'single rest parameter': 152 | { type: 'Script', 153 | start: 0, 154 | end: 16, 155 | statements: 156 | [ { type: 'ExpressionStatement', 157 | start: 0, 158 | end: 16, 159 | expression: 160 | { type: 'ArrowFunction', 161 | start: 0, 162 | end: 16, 163 | kind: '', 164 | params: 165 | [ { type: 'RestParameter', 166 | start: 1, 167 | end: 8, 168 | identifier: 169 | { type: 'Identifier', 170 | start: 4, 171 | end: 8, 172 | value: 'args', 173 | context: 'declaration' } } ], 174 | body: { type: 'FunctionBody', start: 13, end: 16, statements: [] } } } ] }, 175 | 176 | /** (x, ...args) => { } **/ 177 | 'rest parameter with multiple parameters': 178 | { type: 'Script', 179 | start: 0, 180 | end: 19, 181 | statements: 182 | [ { type: 'ExpressionStatement', 183 | start: 0, 184 | end: 19, 185 | expression: 186 | { type: 'ArrowFunction', 187 | start: 0, 188 | end: 19, 189 | kind: '', 190 | params: 191 | [ { type: 'FormalParameter', 192 | start: 1, 193 | end: 2, 194 | pattern: 195 | { type: 'Identifier', 196 | start: 1, 197 | end: 2, 198 | value: 'x', 199 | context: 'declaration' }, 200 | initializer: null }, 201 | { type: 'RestParameter', 202 | start: 4, 203 | end: 11, 204 | identifier: 205 | { type: 'Identifier', 206 | start: 7, 207 | end: 11, 208 | value: 'args', 209 | context: 'declaration' } } ], 210 | body: { type: 'FunctionBody', start: 16, end: 19, statements: [] } } } ] }, 211 | 212 | /** (...1 + 1) => 1 **/ 213 | 'rest args can only be binding identifiers': {}, 214 | 215 | /** (...a, b) => 1 **/ 216 | 'rest parameter must be final parameter': {}, 217 | 218 | /** (a, ...b); **/ 219 | 'rest is not allowed in paren expressions': {}, 220 | 221 | /** "use strict"; (arguments) => {} **/ 222 | 'binding to arguments is disallowed in strict mode': {}, 223 | 224 | /** "use strict"; (...arguments) => {} **/ 225 | 'binding rest parameter to arguments is disallowed in strict mode': {}, 226 | 227 | /** "use strict"; ({ arguments }) => {} **/ 228 | 'binding to arguments with destructuring is disallowed in strict mode (1)': {}, 229 | 230 | /** "use strict"; ({ args: arguments }) => {} **/ 231 | 'binding to arguments with destructuring is disallowed in strict mode (2)': {}, 232 | 233 | /** x => { "use strict"; delete x; } **/ 234 | '"use strict" prologue sets strictness of function': {}, 235 | 236 | /** (x = (delete x)) => { "use strict"; } **/ 237 | '"use strict" prologue sets strictness of default expressions': {}, 238 | 239 | /** for (x => x in y;;); **/ 240 | 'arrow functions are restricted by no-in': {}, 241 | 242 | /** x || x => x **/ 243 | 'arrow has correct precedence': {}, 244 | 245 | /** x 246 | => x **/ 247 | 'new line restriction before arrow (1)': {}, 248 | 249 | /** (x) 250 | => x **/ 251 | 'new line restriction before arrow (2)': {}, 252 | 253 | /** () 254 | => x **/ 255 | 'new line restriction before arrow (3)': {}, 256 | 257 | /** () + 1 **/ 258 | 'empty parens not allowed without an arrow': {}, 259 | 260 | 261 | }) 262 | -------------------------------------------------------------------------------- /util/generate-unicode.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | === Unicode Test Generator for ES6 Parsers === 4 | 5 | When parsing ES6, there are three Unicode character classifications 6 | that we are interested in: 7 | 8 | - Whitespace: In addition to the whitespace characters explicitly 9 | defined in the ES6 specification, we also need to recognize any 10 | character with a Unicode general category of 'Zs'. The predefined 11 | whitespace characters are: 12 | 13 | - U+0009 [\t] 14 | - U+000B [\v] 15 | - U+000C [\f] 16 | - U+0020 [ ] 17 | - U+00A0 18 | - U+FEFF 19 | 20 | In order to find the other whitespace characters, we download the 21 | text file located at http://www.unicode.org/Public/UNIDATA/PropList.txt 22 | and find all of the character ranges with the 'White_Space' property 23 | and whose general category is 'Zs'. 24 | 25 | - IdentifierStart and IdentifierPart: In addition to the identifier 26 | characters explicitly defined in the ES6 specification, we also need 27 | to recognize characters with a Unicode derived property of 'ID_Start' 28 | or 'ID_Continue'. The predefined identifier characters are: 29 | 30 | - U+0024 [$] IdentifierStart 31 | - U+005F [_] IdentifierStart 32 | - U+200C IdentifierPart 33 | - U+200D IdentifierPart 34 | 35 | In order to find the other identifier characters, we download the 36 | text file located at http://www.unicode.org/Public/UNIDATA/DerivedCoreProperties.txt 37 | and find all of the character ranges with a Unicode derived property 38 | of 'ID_Start' or 'ID_Continue' which can be represented in UTF-16 as 39 | a single code unit. 40 | 41 | After obtaining the list of valid code points for whitespace and identifiers, 42 | we build a list of ranges and output code 43 | 44 | */ 45 | 46 | const Path = require('path'); 47 | const HTTP = require('http'); 48 | const FS = require('fs'); 49 | 50 | function log(msg) { 51 | console.log('...' + msg); 52 | } 53 | 54 | function downloadDatabase(url, filePath, callback) { 55 | let filename = Path.basename(filePath); 56 | 57 | if (FS.existsSync(filePath)) { 58 | log(filename + ' database already downloaded'); 59 | return callback(); 60 | } 61 | 62 | log('Downloading ' + filename + ' database'); 63 | 64 | HTTP.get(url, response => { 65 | let out = FS.createWriteStream(filePath); 66 | response.pipe(out); 67 | response.on('end', callback); 68 | }).on('error', fail); 69 | } 70 | 71 | function getUnicodeVersion(data) { 72 | let version = /^#[^\n\d]*(\d+(?:\.\d+)*)/; 73 | let date = /\n#\s*Date:\s*(.+)/; 74 | let match; 75 | 76 | if (match = version.exec(data)) 77 | unicodeVersion = match[1]; 78 | 79 | if (match = date.exec(data)) 80 | unicodeDate = match[1]; 81 | } 82 | 83 | function foldRanges(map) { 84 | let ranges = []; 85 | let range; 86 | 87 | Object.keys(map).sort((a, b) => +(a) - b).forEach(val => { 88 | // Convert val to a number 89 | val = +val; 90 | 91 | if (range && val === range.to + 1 && map[val] === range.type) { 92 | range.to = val; 93 | } else { 94 | range = { 95 | from: val, 96 | to: val, 97 | type: map[val], 98 | }; 99 | ranges.push(range); 100 | } 101 | }); 102 | 103 | return ranges; 104 | } 105 | 106 | let unicodeVersion = ''; 107 | let unicodeDate = ''; 108 | let wsRanges; 109 | let idRanges; 110 | 111 | const BASE_URL = 'http://www.unicode.org/Public/UNIDATA'; 112 | const PROP_LIST_URL = `${ BASE_URL }/PropList.txt`; 113 | const DERIVED_PROPS_URL = `${ BASE_URL }/DerivedCoreProperties.txt`; 114 | 115 | const WORK_FOLDER = Path.resolve(__dirname, '_unicode'); 116 | const PROP_LIST_PATH = Path.resolve(WORK_FOLDER, 'PropList.txt'); 117 | const DERIVED_PROPS_PATH = Path.resolve(WORK_FOLDER, 'DerivedCoreProperties.txt'); 118 | const JS_OUT_PATH = Path.resolve(WORK_FOLDER, 'UnicodeData.js'); 119 | 120 | // Whitespace 121 | const CP_TAB = 0x09; 122 | const CP_VT = 0x0b; 123 | const CP_FF = 0x0c; 124 | const CP_SP = 0x20; 125 | const CP_NBSP = 0xa0; 126 | const CP_BOM = 0xfeff; 127 | 128 | // Identifiers 129 | const CP_DOLLAR = 0x24; 130 | const CP_UNDERSCORE = 0x5f; 131 | const CP_ZWNJ = 0x200c; 132 | const CP_ZWJ = 0x200d; 133 | 134 | // Code Point Types 135 | const WHITE_SPACE = 1; 136 | const ID_START = 2; 137 | const ID_CONT = 3; 138 | 139 | const Tasks = { 140 | start() { 141 | log('Unicode Test Generator for ES6 Parsers'); 142 | nextTask(); 143 | }, 144 | 145 | createWorkFolder() { 146 | if (!FS.existsSync(WORK_FOLDER)) { 147 | log('Creating work folder'); 148 | FS.mkdirSync(WORK_FOLDER); 149 | } 150 | nextTask(); 151 | }, 152 | 153 | getPropList() { 154 | downloadDatabase(PROP_LIST_URL, PROP_LIST_PATH, nextTask); 155 | }, 156 | 157 | getDerivedProps() { 158 | downloadDatabase(DERIVED_PROPS_URL, DERIVED_PROPS_PATH, nextTask); 159 | }, 160 | 161 | loadWhiteSpaceChars() { 162 | let wsMap = {}; 163 | 164 | // Load pre-defined whitespace code points 165 | wsMap[CP_TAB] = 166 | wsMap[CP_VT] = 167 | wsMap[CP_FF] = 168 | wsMap[CP_SP] = 169 | wsMap[CP_NBSP] = 170 | wsMap[CP_BOM] = WHITE_SPACE; 171 | 172 | log('Opening PropList.txt database'); 173 | 174 | let propList = FS.readFileSync(PROP_LIST_PATH, { 175 | encoding: 'utf8' 176 | }); 177 | 178 | let pattern = /\n([a-fA-F0-9\.]+)\s+;\s*White_Space\s*#\s*Zs/g; 179 | let match; 180 | 181 | getUnicodeVersion(propList); 182 | 183 | log('Unicode ' + unicodeVersion + ' | ' + unicodeDate); 184 | log('Scanning PropList.txt for White_Space (Zs)'); 185 | 186 | while (match = pattern.exec(propList)) { 187 | let range = match[1].split('..').map(val => parseInt(val, 16)); 188 | 189 | if (range.length === 1) 190 | range[1] = range[0]; 191 | 192 | for (let code = range[0]; code <= range[1]; ++code) 193 | wsMap[code] = WHITE_SPACE; 194 | } 195 | 196 | wsRanges = foldRanges(wsMap); 197 | 198 | nextTask(); 199 | }, 200 | 201 | loadIdentChars() { 202 | let idMap = {}; 203 | 204 | // Load pre-defined identifier code points 205 | idMap[CP_DOLLAR] = ID_START; 206 | idMap[CP_UNDERSCORE] = ID_START; 207 | idMap[CP_ZWNJ] = ID_CONT; 208 | idMap[CP_ZWJ] = ID_CONT; 209 | 210 | log('Opening DerivedCoreProperties.txt database'); 211 | 212 | let propList = FS.readFileSync(DERIVED_PROPS_PATH, { 213 | encoding: 'utf8' 214 | }); 215 | 216 | let pattern = /\n([a-fA-F0-9\.]+)\s+;\s*ID_(Start|Continue)\s+/g; 217 | let match; 218 | 219 | log('Scanning DerivedCoreProperties.txt for ID_Start and ID_Continue'); 220 | 221 | while (match = pattern.exec(propList)) { 222 | let range = match[1].split('..').map(val => parseInt(val, 16)); 223 | 224 | if (range.length === 1) 225 | range[1] = range[0]; 226 | 227 | for (let code = range[0]; code <= range[1]; ++code) 228 | if (idMap[code] !== ID_START) 229 | idMap[code] = match[2] === 'Start' ? ID_START : ID_CONT; 230 | } 231 | 232 | idRanges = foldRanges(idMap); 233 | 234 | nextTask(); 235 | }, 236 | 237 | exportJS() { 238 | log('Generating javascript code'); 239 | 240 | function rangesToArrayString(ranges) { 241 | return '[\n' + 242 | ranges.map(range => { 243 | return ` ${ range.from }, ${ range.to - range.from }, ${ range.type },\n`; 244 | }).join('') + 245 | ']'; 246 | } 247 | 248 | let out = '// Unicode ' + unicodeVersion + ' | ' + unicodeDate + '\n\n' + 249 | 'export const IDENTIFIER = ' + rangesToArrayString(idRanges) + ';\n\n' + 250 | 'export const WHITESPACE = ' + rangesToArrayString(wsRanges) + ';\n'; 251 | 252 | FS.writeFileSync(JS_OUT_PATH, out); 253 | 254 | nextTask(); 255 | }, 256 | }; 257 | 258 | Tasks.list = Object.keys(Tasks); 259 | 260 | function nextTask() { 261 | if (Tasks.list.length === 0) 262 | return complete(); 263 | 264 | let next = Tasks.list.shift(); 265 | Tasks[next](); 266 | } 267 | 268 | function fail(err) { 269 | log('Oops!', err); 270 | } 271 | 272 | function complete() { 273 | log('Finished.'); 274 | } 275 | 276 | nextTask(); 277 | -------------------------------------------------------------------------------- /test/parse/expressions/object-computed-names.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** ({ [x]: 1 }) **/ 4 | 'computed data properties': 5 | { type: 'Script', 6 | start: 0, 7 | end: 12, 8 | statements: 9 | [ { type: 'ExpressionStatement', 10 | start: 0, 11 | end: 12, 12 | expression: 13 | { type: 'ParenExpression', 14 | start: 0, 15 | end: 12, 16 | expression: 17 | { type: 'ObjectLiteral', 18 | start: 1, 19 | end: 11, 20 | properties: 21 | [ { type: 'PropertyDefinition', 22 | start: 3, 23 | end: 9, 24 | name: 25 | { type: 'ComputedPropertyName', 26 | start: 3, 27 | end: 6, 28 | expression: 29 | { type: 'Identifier', 30 | start: 4, 31 | end: 5, 32 | value: 'x', 33 | context: 'variable' } }, 34 | expression: { type: 'NumberLiteral', start: 8, end: 9, value: 1 } } ], 35 | trailingComma: false } } } ] }, 36 | 37 | /** ({ [x]() {} }) **/ 38 | 'computed method names': 39 | { type: 'Script', 40 | start: 0, 41 | end: 14, 42 | statements: 43 | [ { type: 'ExpressionStatement', 44 | start: 0, 45 | end: 14, 46 | expression: 47 | { type: 'ParenExpression', 48 | start: 0, 49 | end: 14, 50 | expression: 51 | { type: 'ObjectLiteral', 52 | start: 1, 53 | end: 13, 54 | properties: 55 | [ { type: 'MethodDefinition', 56 | start: 3, 57 | end: 11, 58 | static: false, 59 | kind: '', 60 | name: 61 | { type: 'ComputedPropertyName', 62 | start: 3, 63 | end: 6, 64 | expression: 65 | { type: 'Identifier', 66 | start: 4, 67 | end: 5, 68 | value: 'x', 69 | context: 'variable' } }, 70 | params: [], 71 | body: { type: 'FunctionBody', start: 9, end: 11, statements: [] } } ], 72 | trailingComma: false } } } ] }, 73 | 74 | /** ({ get [x]() {}, set [x](value) {} }) **/ 75 | 'computed accessors': 76 | { type: 'Script', 77 | start: 0, 78 | end: 37, 79 | statements: 80 | [ { type: 'ExpressionStatement', 81 | start: 0, 82 | end: 37, 83 | expression: 84 | { type: 'ParenExpression', 85 | start: 0, 86 | end: 37, 87 | expression: 88 | { type: 'ObjectLiteral', 89 | start: 1, 90 | end: 36, 91 | properties: 92 | [ { type: 'MethodDefinition', 93 | start: 3, 94 | end: 15, 95 | static: false, 96 | kind: 'get', 97 | name: 98 | { type: 'ComputedPropertyName', 99 | start: 7, 100 | end: 10, 101 | expression: 102 | { type: 'Identifier', 103 | start: 8, 104 | end: 9, 105 | value: 'x', 106 | context: 'variable' } }, 107 | params: [], 108 | body: { type: 'FunctionBody', start: 13, end: 15, statements: [] } }, 109 | { type: 'MethodDefinition', 110 | start: 17, 111 | end: 34, 112 | static: false, 113 | kind: 'set', 114 | name: 115 | { type: 'ComputedPropertyName', 116 | start: 21, 117 | end: 24, 118 | expression: 119 | { type: 'Identifier', 120 | start: 22, 121 | end: 23, 122 | value: 'x', 123 | context: 'variable' } }, 124 | params: 125 | [ { type: 'FormalParameter', 126 | start: 25, 127 | end: 30, 128 | pattern: 129 | { type: 'Identifier', 130 | start: 25, 131 | end: 30, 132 | value: 'value', 133 | context: 'declaration' }, 134 | initializer: null } ], 135 | body: { type: 'FunctionBody', start: 32, end: 34, statements: [] } } ], 136 | trailingComma: false } } } ] } 137 | , 138 | 139 | /** ({ *[x]() {} }) **/ 140 | 'computed generators': 141 | { type: 'Script', 142 | start: 0, 143 | end: 15, 144 | statements: 145 | [ { type: 'ExpressionStatement', 146 | start: 0, 147 | end: 15, 148 | expression: 149 | { type: 'ParenExpression', 150 | start: 0, 151 | end: 15, 152 | expression: 153 | { type: 'ObjectLiteral', 154 | start: 1, 155 | end: 14, 156 | properties: 157 | [ { type: 'MethodDefinition', 158 | start: 3, 159 | end: 12, 160 | static: false, 161 | kind: 'generator', 162 | name: 163 | { type: 'ComputedPropertyName', 164 | start: 4, 165 | end: 7, 166 | expression: 167 | { type: 'Identifier', 168 | start: 5, 169 | end: 6, 170 | value: 'x', 171 | context: 'variable' } }, 172 | params: [], 173 | body: { type: 'FunctionBody', start: 10, end: 12, statements: [] } } ], 174 | trailingComma: false } } } ] }, 175 | 176 | /** "use strict"; ({ [x]: 1, [x]: 1 }) **/ 177 | 'computed names are not checked for duplicate keys': 178 | { type: 'Script', 179 | start: 0, 180 | end: 34, 181 | statements: 182 | [ { type: 'Directive', 183 | start: 0, 184 | end: 13, 185 | value: 'use strict', 186 | expression: { type: 'StringLiteral', start: 0, end: 12, value: 'use strict' } }, 187 | { type: 'ExpressionStatement', 188 | start: 14, 189 | end: 34, 190 | expression: 191 | { type: 'ParenExpression', 192 | start: 14, 193 | end: 34, 194 | expression: 195 | { type: 'ObjectLiteral', 196 | start: 15, 197 | end: 33, 198 | properties: 199 | [ { type: 'PropertyDefinition', 200 | start: 17, 201 | end: 23, 202 | name: 203 | { type: 'ComputedPropertyName', 204 | start: 17, 205 | end: 20, 206 | expression: 207 | { type: 'Identifier', 208 | start: 18, 209 | end: 19, 210 | value: 'x', 211 | context: 'variable' } }, 212 | expression: { type: 'NumberLiteral', start: 22, end: 23, value: 1 } }, 213 | { type: 'PropertyDefinition', 214 | start: 25, 215 | end: 31, 216 | name: 217 | { type: 'ComputedPropertyName', 218 | start: 25, 219 | end: 28, 220 | expression: 221 | { type: 'Identifier', 222 | start: 26, 223 | end: 27, 224 | value: 'x', 225 | context: 'variable' } }, 226 | expression: { type: 'NumberLiteral', start: 30, end: 31, value: 1 } } ], 227 | trailingComma: false } } } ] }, 228 | 229 | }) 230 | -------------------------------------------------------------------------------- /test/parse/statements/for-in-of.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** for (x in y); **/ 4 | 'for-in': 5 | { type: 'Script', 6 | start: 0, 7 | end: 13, 8 | statements: 9 | [ { type: 'ForInStatement', 10 | start: 0, 11 | end: 13, 12 | left: 13 | { type: 'Identifier', 14 | start: 5, 15 | end: 6, 16 | value: 'x', 17 | context: 'variable' }, 18 | right: 19 | { type: 'Identifier', 20 | start: 10, 21 | end: 11, 22 | value: 'y', 23 | context: 'variable' }, 24 | body: { type: 'EmptyStatement', start: 12, end: 13 } } ] }, 25 | 26 | /** for (var x in y); **/ 27 | 'for-in with var declaration': 28 | { type: 'Script', 29 | start: 0, 30 | end: 17, 31 | statements: 32 | [ { type: 'ForInStatement', 33 | start: 0, 34 | end: 17, 35 | left: 36 | { type: 'VariableDeclaration', 37 | start: 5, 38 | end: 10, 39 | kind: 'var', 40 | declarations: 41 | [ { type: 'VariableDeclarator', 42 | start: 9, 43 | end: 10, 44 | pattern: 45 | { type: 'Identifier', 46 | start: 9, 47 | end: 10, 48 | value: 'x', 49 | context: 'declaration' }, 50 | initializer: null } ] }, 51 | right: 52 | { type: 'Identifier', 53 | start: 14, 54 | end: 15, 55 | value: 'y', 56 | context: 'variable' }, 57 | body: { type: 'EmptyStatement', start: 16, end: 17 } } ] }, 58 | 59 | /** for (let x in y); **/ 60 | 'for-in with let declaration': 61 | { type: 'Script', 62 | start: 0, 63 | end: 17, 64 | statements: 65 | [ { type: 'ForInStatement', 66 | start: 0, 67 | end: 17, 68 | left: 69 | { type: 'VariableDeclaration', 70 | start: 5, 71 | end: 10, 72 | kind: 'let', 73 | declarations: 74 | [ { type: 'VariableDeclarator', 75 | start: 9, 76 | end: 10, 77 | pattern: 78 | { type: 'Identifier', 79 | start: 9, 80 | end: 10, 81 | value: 'x', 82 | context: 'declaration' }, 83 | initializer: null } ] }, 84 | right: 85 | { type: 'Identifier', 86 | start: 14, 87 | end: 15, 88 | value: 'y', 89 | context: 'variable' }, 90 | body: { type: 'EmptyStatement', start: 16, end: 17 } } ] }, 91 | 92 | /** for (let {x} in y); **/ 93 | 'for-in with pattern variable declarator': 94 | { type: 'Script', 95 | start: 0, 96 | end: 19, 97 | statements: 98 | [ { type: 'ForInStatement', 99 | start: 0, 100 | end: 19, 101 | left: 102 | { type: 'VariableDeclaration', 103 | start: 5, 104 | end: 12, 105 | kind: 'let', 106 | declarations: 107 | [ { type: 'VariableDeclarator', 108 | start: 9, 109 | end: 12, 110 | pattern: 111 | { type: 'ObjectPattern', 112 | start: 9, 113 | end: 12, 114 | properties: 115 | [ { type: 'PatternProperty', 116 | start: 10, 117 | end: 11, 118 | name: 119 | { type: 'Identifier', 120 | start: 10, 121 | end: 11, 122 | value: 'x', 123 | context: 'declaration' }, 124 | pattern: null, 125 | initializer: null } ], 126 | trailingComma: false }, 127 | initializer: null } ] }, 128 | right: 129 | { type: 'Identifier', 130 | start: 16, 131 | end: 17, 132 | value: 'y', 133 | context: 'variable' }, 134 | body: { type: 'EmptyStatement', start: 18, end: 19 } } ] }, 135 | 136 | /** for (var i = 0 in x); **/ 137 | 'initializer allowed in for-in head in sloppy mode': 138 | { 139 | type: 'Script', 140 | start: 0, 141 | end: 21, 142 | statements: 143 | [ { 144 | type: 'ForInStatement', 145 | start: 0, 146 | end: 21, 147 | left: 148 | { 149 | type: 'VariableDeclaration', 150 | start: 5, 151 | end: 14, 152 | kind: 'var', 153 | declarations: 154 | [ { 155 | type: 'VariableDeclarator', 156 | start: 9, 157 | end: 14, 158 | pattern: 159 | { 160 | type: 'Identifier', 161 | start: 9, 162 | end: 10, 163 | value: 'i', 164 | context: 'declaration' }, 165 | initializer: { type: 'NumberLiteral', start: 13, end: 14, value: 0 } } ], 166 | error: 'Invalid initializer in for-in statement' }, 167 | right: 168 | { 169 | type: 'Identifier', 170 | start: 18, 171 | end: 19, 172 | value: 'x', 173 | context: 'variable' }, 174 | body: { type: 'EmptyStatement', start: 20, end: 21 } } ] }, 175 | 176 | /** 'use strict'; for (var i = 0 in x); **/ 177 | 'initializer in for-in not allowed in strict mode': 178 | {}, 179 | 180 | /** for (var x of y); **/ 181 | 'for-of with declaration': 182 | { type: 'Script', 183 | start: 0, 184 | end: 17, 185 | statements: 186 | [ { type: 'ForOfStatement', 187 | async: false, 188 | start: 0, 189 | end: 17, 190 | left: 191 | { type: 'VariableDeclaration', 192 | start: 5, 193 | end: 10, 194 | kind: 'var', 195 | declarations: 196 | [ { type: 'VariableDeclarator', 197 | start: 9, 198 | end: 10, 199 | pattern: 200 | { type: 'Identifier', 201 | start: 9, 202 | end: 10, 203 | value: 'x', 204 | context: 'declaration' }, 205 | initializer: null } ] }, 206 | right: 207 | { type: 'Identifier', 208 | start: 14, 209 | end: 15, 210 | value: 'y', 211 | context: 'variable' }, 212 | body: { type: 'EmptyStatement', start: 16, end: 17 } } ] }, 213 | 214 | /** for (x of y); **/ 215 | 'for-of no declaration': 216 | { type: 'Script', 217 | start: 0, 218 | end: 13, 219 | statements: 220 | [ { type: 'ForOfStatement', 221 | async: false, 222 | start: 0, 223 | end: 13, 224 | left: 225 | { type: 'Identifier', 226 | start: 5, 227 | end: 6, 228 | value: 'x', 229 | context: 'variable' }, 230 | right: 231 | { type: 'Identifier', 232 | start: 10, 233 | end: 11, 234 | value: 'y', 235 | context: 'variable' }, 236 | body: { type: 'EmptyStatement', start: 12, end: 13 } } ] }, 237 | 238 | /** for (const x of y); **/ 239 | 'for-of with const declaration': { type: 'Script', 240 | start: 0, 241 | end: 19, 242 | statements: 243 | [ { type: 'ForOfStatement', 244 | async: false, 245 | start: 0, 246 | end: 19, 247 | left: 248 | { type: 'VariableDeclaration', 249 | start: 5, 250 | end: 12, 251 | kind: 'const', 252 | declarations: 253 | [ { type: 'VariableDeclarator', 254 | start: 11, 255 | end: 12, 256 | pattern: 257 | { type: 'Identifier', 258 | start: 11, 259 | end: 12, 260 | value: 'x', 261 | context: 'declaration' }, 262 | initializer: null } ] }, 263 | right: 264 | { type: 'Identifier', 265 | start: 16, 266 | end: 17, 267 | value: 'y', 268 | context: 'variable' }, 269 | body: { type: 'EmptyStatement', start: 18, end: 19 } } ] }, 270 | 271 | /** for (let [x, y] = foo of z); **/ 272 | 'initializers not allowed in for-of head': {}, 273 | 274 | }) 275 | -------------------------------------------------------------------------------- /test/parse/expressions/object-rest-spread.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** ({ a: 1, ...b, c: 2, ...d }) **/ 4 | 'spread properties': { 5 | type: 'Script', 6 | start: 0, 7 | end: 28, 8 | statements: 9 | [ { 10 | type: 'ExpressionStatement', 11 | start: 0, 12 | end: 28, 13 | expression: 14 | { 15 | type: 'ParenExpression', 16 | start: 0, 17 | end: 28, 18 | expression: 19 | { 20 | type: 'ObjectLiteral', 21 | start: 1, 22 | end: 27, 23 | properties: 24 | [ { 25 | type: 'PropertyDefinition', 26 | start: 3, 27 | end: 7, 28 | name: { type: 'Identifier', start: 3, end: 4, value: 'a', context: '' }, 29 | expression: { type: 'NumberLiteral', start: 6, end: 7, value: 1 } }, 30 | { 31 | type: 'SpreadExpression', 32 | start: 9, 33 | end: 13, 34 | expression: 35 | { 36 | type: 'Identifier', 37 | start: 12, 38 | end: 13, 39 | value: 'b', 40 | context: 'variable' } }, 41 | { 42 | type: 'PropertyDefinition', 43 | start: 15, 44 | end: 19, 45 | name: { type: 'Identifier', start: 15, end: 16, value: 'c', context: '' }, 46 | expression: { type: 'NumberLiteral', start: 18, end: 19, value: 2 } }, 47 | { 48 | type: 'SpreadExpression', 49 | start: 21, 50 | end: 25, 51 | expression: 52 | { 53 | type: 'Identifier', 54 | start: 24, 55 | end: 25, 56 | value: 'd', 57 | context: 'variable' } } ], 58 | trailingComma: false } } } ] }, 59 | 60 | /** let { a, ...b } = x; **/ 61 | 'rest binding patterns': { 62 | type: 'Script', 63 | start: 0, 64 | end: 20, 65 | statements: 66 | [ { 67 | type: 'VariableDeclaration', 68 | start: 0, 69 | end: 20, 70 | kind: 'let', 71 | declarations: 72 | [ { 73 | type: 'VariableDeclarator', 74 | start: 4, 75 | end: 19, 76 | pattern: 77 | { 78 | type: 'ObjectPattern', 79 | start: 4, 80 | end: 15, 81 | properties: 82 | [ { 83 | type: 'PatternProperty', 84 | start: 6, 85 | end: 7, 86 | name: 87 | { 88 | type: 'Identifier', 89 | start: 6, 90 | end: 7, 91 | value: 'a', 92 | context: 'declaration' }, 93 | pattern: null, 94 | initializer: null }, 95 | { 96 | type: 'PatternRestElement', 97 | start: 9, 98 | end: 13, 99 | pattern: 100 | { 101 | type: 'Identifier', 102 | start: 12, 103 | end: 13, 104 | value: 'b', 105 | context: 'declaration' } } ], 106 | trailingComma: false }, 107 | initializer: 108 | { 109 | type: 'Identifier', 110 | start: 18, 111 | end: 19, 112 | value: 'x', 113 | context: 'variable' } } ] } ] }, 114 | 115 | /** ({ a, ...b } = x); **/ 116 | 'rest assignment patterns': { 117 | type: 'Script', 118 | start: 0, 119 | end: 18, 120 | statements: 121 | [ { 122 | type: 'ExpressionStatement', 123 | start: 0, 124 | end: 18, 125 | expression: 126 | { 127 | type: 'ParenExpression', 128 | start: 0, 129 | end: 17, 130 | expression: 131 | { 132 | type: 'AssignmentExpression', 133 | start: 1, 134 | end: 16, 135 | operator: '=', 136 | left: 137 | { 138 | type: 'ObjectPattern', 139 | start: 1, 140 | end: 12, 141 | properties: 142 | [ { 143 | type: 'PatternProperty', 144 | start: 3, 145 | end: 4, 146 | name: 147 | { 148 | type: 'Identifier', 149 | start: 3, 150 | end: 4, 151 | value: 'a', 152 | context: 'variable' }, 153 | pattern: null, 154 | initializer: null }, 155 | { 156 | type: 'PatternRestElement', 157 | start: 6, 158 | end: 10, 159 | pattern: 160 | { 161 | type: 'Identifier', 162 | start: 9, 163 | end: 10, 164 | value: 'b', 165 | context: 'variable' } } ], 166 | trailingComma: false }, 167 | right: 168 | { 169 | type: 'Identifier', 170 | start: 15, 171 | end: 16, 172 | value: 'x', 173 | context: 'variable' } } } } ] }, 174 | 175 | /** ({ ...this.x } = x); **/ 176 | 'rest assignment property may be member expression': { 177 | type: 'Script', 178 | start: 0, 179 | end: 20, 180 | statements: 181 | [ { 182 | type: 'ExpressionStatement', 183 | start: 0, 184 | end: 20, 185 | expression: 186 | { 187 | type: 'ParenExpression', 188 | start: 0, 189 | end: 19, 190 | expression: 191 | { 192 | type: 'AssignmentExpression', 193 | start: 1, 194 | end: 18, 195 | operator: '=', 196 | left: 197 | { 198 | type: 'ObjectPattern', 199 | start: 1, 200 | end: 14, 201 | properties: 202 | [ { 203 | type: 'PatternRestElement', 204 | start: 3, 205 | end: 12, 206 | pattern: 207 | { 208 | type: 'MemberExpression', 209 | start: 6, 210 | end: 12, 211 | object: { type: 'ThisExpression', start: 6, end: 10 }, 212 | property: { type: 'Identifier', start: 11, end: 12, value: 'x', context: '' } } } ], 213 | trailingComma: false }, 214 | right: 215 | { 216 | type: 'Identifier', 217 | start: 17, 218 | end: 18, 219 | value: 'x', 220 | context: 'variable' } } } } ] }, 221 | 222 | /** let { ...y, } = {}; **/ 223 | 'trailing comma not allowed after rest binding property': {}, 224 | 225 | /** let { ...y, a } = {}; **/ 226 | 'rest binding property must be last pattern': {}, 227 | 228 | /** let { ...{ a } } = {}; **/ 229 | 'rest binding property must not be an object pattern': {}, 230 | 231 | /** let { ...[a] } = {}; **/ 232 | 'rest binding property must not be an array pattern': {}, 233 | 234 | /** ({ ...{ a } } = x); **/ 235 | 'rest assignment property must not be an object pattern': {}, 236 | 237 | /** ({ ...[a] } = x); **/ 238 | 'rest assignment property must not be an array pattern': {}, 239 | 240 | }) 241 | -------------------------------------------------------------------------------- /src/ScopeResolver.js: -------------------------------------------------------------------------------- 1 | import { forEachChild } from './AST.js'; 2 | 3 | const VarNames = Symbol(); 4 | 5 | class Scope { 6 | 7 | constructor(type, strict, node = null) { 8 | this.type = type; 9 | this.node = node; 10 | this.strict = strict; 11 | this.names = new Map(); 12 | this.free = []; 13 | this.parent = null; 14 | this.children = []; 15 | this[VarNames] = []; 16 | } 17 | 18 | resolveName(name) { 19 | let record = this.names.get(name); 20 | if (record) return record; 21 | if (this.parent) return this.parent.resolveName(name); 22 | return null; 23 | } 24 | 25 | } 26 | 27 | export class ScopeResolver { 28 | 29 | constructor() { 30 | this.stack = []; 31 | this.top = null; 32 | this.lineMap = null; 33 | } 34 | 35 | resolve(ast, options = {}) { 36 | this.lineMap = options.lineMap; 37 | this.top = new Scope('var', false, ast); 38 | this.visit(ast); 39 | this.flushFree(); 40 | this.top[VarNames] = null; 41 | return this.top; 42 | } 43 | 44 | fail(msg, node) { 45 | let err = new SyntaxError(msg); 46 | 47 | if (this.lineMap) { 48 | let loc = this.lineMap.locate(node.start); 49 | err.line = loc.line; 50 | err.column = loc.column; 51 | err.lineOffset = loc.lineOffset; 52 | err.startOffset = node.start; 53 | err.endOffset = node.end; 54 | } 55 | 56 | throw err; 57 | } 58 | 59 | pushScope(type, node) { 60 | let strict = this.top.strict; 61 | this.stack.push(this.top); 62 | return this.top = new Scope(type, strict, node); 63 | } 64 | 65 | flushFree() { 66 | let map = this.top.names; 67 | let free = this.top.free; 68 | let next = null; 69 | let freeList = []; 70 | 71 | if (this.stack.length > 0) 72 | next = this.stack[this.stack.length - 1]; 73 | 74 | this.top.free = freeList; 75 | 76 | free.forEach(r => { 77 | let name = r.value; 78 | let record = map.get(name); 79 | 80 | if (record) { 81 | record.references.push(r); 82 | } else if (next) { 83 | next.free.push(r); 84 | } else { 85 | freeList.push(r); 86 | } 87 | }); 88 | } 89 | 90 | linkScope(child) { 91 | let p = this.top; 92 | child.parent = p; 93 | p.children.push(child); 94 | } 95 | 96 | popScope() { 97 | let scope = this.top; 98 | let varNames = scope[VarNames]; 99 | 100 | scope[VarNames] = null; 101 | 102 | this.flushFree(); 103 | this.top = this.stack.pop(); 104 | this.linkScope(scope); 105 | 106 | varNames.forEach(n => { 107 | if (scope.names.has(n.value)) 108 | this.fail('Cannot shadow lexical declaration with var', n); 109 | else if (this.top.type === 'var') 110 | this.addName(n, 'var'); 111 | else 112 | this.top[VarNames].push(n); 113 | }); 114 | } 115 | 116 | visit(node, kind) { 117 | if (!node) 118 | return; 119 | 120 | let f = this[node.type]; 121 | 122 | if (typeof f === 'function') 123 | f.call(this, node, kind); 124 | else 125 | forEachChild(node, n => this.visit(n, kind)); 126 | } 127 | 128 | hasStrictDirective(statements) { 129 | for (let i = 0; i < statements.length; ++i) { 130 | let n = statements[i]; 131 | 132 | if (n.type !== 'Directive') 133 | break; 134 | 135 | if (n.value === 'use strict') 136 | return true; 137 | } 138 | 139 | return false; 140 | } 141 | 142 | visitFunction(params, body, strictParams) { 143 | let paramScope = this.pushScope('param'); 144 | 145 | if ( 146 | !this.top.strict && 147 | body.statements && 148 | this.hasStrictDirective(body.statements) 149 | ) { 150 | this.top.strict = true; 151 | } 152 | 153 | strictParams = strictParams || this.top.strict; 154 | 155 | params.forEach(n => { 156 | if ( 157 | !strictParams && ( 158 | n.type !== 'FormalParameter' || 159 | n.initializer || 160 | n.pattern.type !== 'Identifier' 161 | ) 162 | ) { 163 | strictParams = true; 164 | } 165 | 166 | this.visit(n, 'param'); 167 | this.flushFree(); 168 | this.top.free.length = 0; 169 | }); 170 | 171 | this.pushScope('var', body); 172 | let blockScope = this.pushScope('block', body); 173 | this.visit(body, 'var'); 174 | this.popScope(); // block 175 | this.popScope(); // var 176 | this.popScope(); // param 177 | 178 | paramScope.names.forEach((record, name) => { 179 | if (blockScope.names.has(name)) 180 | this.fail('Duplicate block declaration', blockScope.names.get(name).declarations[0]); 181 | 182 | if (strictParams && record.declarations.length > 1) 183 | this.fail('Duplicate parameter names', record.declarations[1]); 184 | }); 185 | } 186 | 187 | addReference(node) { 188 | let name = node.value; 189 | let record = this.top.names.get(name); 190 | 191 | if (record) record.references.push(node); 192 | else this.top.free.push(node); 193 | } 194 | 195 | addName(node, kind) { 196 | let name = node.value; 197 | let record = this.top.names.get(name); 198 | 199 | if (record) { 200 | 201 | if (kind !== 'var' && kind !== 'param') 202 | this.fail('Duplicate variable declaration', node); 203 | 204 | } else { 205 | 206 | if (name === 'let' && (kind === 'let' || kind === 'const')) 207 | this.fail('Invalid binding identifier', node); 208 | 209 | this.top.names.set(name, record = { 210 | declarations: [], 211 | references: [], 212 | const: kind === 'const', 213 | }); 214 | } 215 | 216 | record.declarations.push(node); 217 | } 218 | 219 | Script(node) { 220 | if (this.hasStrictDirective(node.statements)) 221 | this.top.strict = true; 222 | 223 | this.pushScope('block', node); 224 | forEachChild(node, n => this.visit(n, 'var')); 225 | this.popScope(); 226 | } 227 | 228 | Module(node) { 229 | this.top.strict = true; 230 | this.pushScope('block', node); 231 | forEachChild(node, n => this.visit(n, 'var')); 232 | this.popScope(); 233 | } 234 | 235 | Block(node) { 236 | this.pushScope('block', node); 237 | forEachChild(node, n => this.visit(n)); 238 | this.popScope(); 239 | } 240 | 241 | SwitchStatement(node) { 242 | this.Block(node); 243 | } 244 | 245 | ForOfStatement(node) { 246 | this.ForStatement(node); 247 | } 248 | 249 | ForInStatement(node) { 250 | this.ForStatement(node); 251 | } 252 | 253 | ForStatement(node) { 254 | this.pushScope('for', node); 255 | forEachChild(node, n => this.visit(n)); 256 | this.popScope(); 257 | } 258 | 259 | CatchClause(node) { 260 | this.pushScope('catch', node); 261 | forEachChild(node, n => this.visit(n)); 262 | this.popScope(); 263 | } 264 | 265 | WithStatement(node) { 266 | this.visit(node.object); 267 | this.pushScope('with', node); 268 | this.visit(node.body); 269 | this.popScope(); 270 | } 271 | 272 | VariableDeclaration(node) { 273 | forEachChild(node, n => this.visit(n, node.kind)); 274 | } 275 | 276 | ImportDeclaration(node) { 277 | forEachChild(node, n => this.visit(n, 'const')); 278 | } 279 | 280 | FunctionDeclaration(node, kind) { 281 | this.visit(node.identifier, kind); 282 | this.pushScope('function', node); 283 | this.visitFunction(node.params, node.body, false); 284 | this.popScope(); 285 | } 286 | 287 | FunctionExpression(node) { 288 | this.pushScope('function', node); 289 | this.visit(node.identifier); 290 | this.visitFunction(node.params, node.body, false); 291 | this.popScope(); 292 | } 293 | 294 | MethodDefinition(node) { 295 | this.pushScope('function', node); 296 | this.visitFunction(node.params, node.body, true); 297 | this.popScope(); 298 | } 299 | 300 | ArrowFunction(node) { 301 | this.pushScope('function', node); 302 | this.visitFunction(node.params, node.body, true); 303 | this.popScope(); 304 | } 305 | 306 | ClassDeclaration(node) { 307 | this.visit(node.identifier, 'let'); 308 | this.pushScope('class', node); 309 | this.top.strict = true; 310 | this.visit(node.base); 311 | this.visit(node.body); 312 | this.popScope(); 313 | } 314 | 315 | ClassExpression(node) { 316 | this.pushScope('class', node); 317 | this.top.strict = true; 318 | this.visit(node.identifier); 319 | this.visit(node.base); 320 | this.visit(node.body); 321 | this.popScope(); 322 | } 323 | 324 | Identifier(node, kind) { 325 | switch (node.context) { 326 | case 'variable': 327 | this.top.free.push(node); 328 | break; 329 | 330 | case 'declaration': 331 | if (kind === 'var' && this.top.type !== 'var') 332 | this.top[VarNames].push(node); 333 | else 334 | this.addName(node, kind); 335 | break; 336 | } 337 | } 338 | 339 | } 340 | -------------------------------------------------------------------------------- /test/parse/functions/generators.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** function *g() {} **/ 4 | "generator declarations": 5 | { type: "Script", 6 | statements: [ 7 | 8 | { type: "FunctionDeclaration", 9 | kind: "generator", 10 | 11 | identifier: 12 | { type: "Identifier", 13 | value: "g" 14 | }, 15 | 16 | params: [], 17 | 18 | body: 19 | { type: "FunctionBody", 20 | statements: [] 21 | } 22 | }] 23 | }, 24 | 25 | /** (function *g() {}); **/ 26 | "generator expresions": 27 | { type: "Script", 28 | statements: [ 29 | 30 | { type: "ExpressionStatement", 31 | expression: 32 | 33 | { type: "ParenExpression", 34 | expression: 35 | 36 | { type: "FunctionExpression", 37 | kind: "generator", 38 | 39 | identifier: 40 | { type: "Identifier", 41 | value: "g" 42 | }, 43 | 44 | params: [], 45 | 46 | body: 47 | { type: "FunctionBody", 48 | statements: [] 49 | } 50 | } 51 | } 52 | }] 53 | }, 54 | 55 | /** function *g() { yield 1; } **/ 56 | "yield is a keyword in generators": 57 | { type: "Script", 58 | statements: [ 59 | 60 | { type: "FunctionDeclaration", 61 | kind: "generator", 62 | 63 | identifier: 64 | { type: "Identifier", 65 | value: "g" 66 | }, 67 | 68 | params: [], 69 | 70 | body: 71 | { type: "FunctionBody", 72 | statements: [ 73 | { type: "ExpressionStatement", 74 | expression: 75 | { type: "YieldExpression", 76 | delegate: false, 77 | expression: 78 | { type: "NumberLiteral", 79 | value: 1 80 | } 81 | } 82 | }] 83 | } 84 | }] 85 | }, 86 | 87 | /** function *g() { (yield) } **/ 88 | "yield expression inside of parens": 89 | { type: "Script", 90 | statements: [ 91 | 92 | { type: "FunctionDeclaration", 93 | kind: "generator", 94 | 95 | identifier: 96 | { type: "Identifier", 97 | value: "g" 98 | }, 99 | 100 | params: [], 101 | 102 | body: 103 | { type: "FunctionBody", 104 | statements: [ 105 | { type: "ExpressionStatement", 106 | expression: 107 | { type: "ParenExpression", 108 | expression: 109 | { type: "YieldExpression", 110 | delegate: false, 111 | expression: null 112 | } 113 | } 114 | }] 115 | } 116 | }] 117 | }, 118 | 119 | /** function *g() { 120 | yield 121 | x } **/ 122 | "yield has no-line-terminator restriction": 123 | { type: "Script", 124 | statements: [ 125 | 126 | { type: "FunctionDeclaration", 127 | kind: "generator", 128 | 129 | identifier: 130 | { type: "Identifier", 131 | value: "g" 132 | }, 133 | 134 | params: [], 135 | 136 | body: 137 | { type: "FunctionBody", 138 | statements: [ 139 | { type: "ExpressionStatement", 140 | expression: 141 | { type: "YieldExpression", 142 | delegate: false, 143 | expression: null 144 | } 145 | }, 146 | { type: "ExpressionStatement", 147 | expression: 148 | { type: "Identifier", 149 | value: "x" 150 | } 151 | }] 152 | } 153 | }] 154 | }, 155 | 156 | /** function *g() { yield 157 | * x } **/ 158 | "no newline between yield and *": {}, 159 | 160 | /** function *g() { -yield; } **/ 161 | "yield is not allowed as an identifier within a generator": {}, 162 | 163 | /** function *g() { var yield; } **/ 164 | "yield is not allowed as a binding identifier within a generator": {}, 165 | 166 | /** function* g(yield) {} **/ 167 | "yield is not allowed as a binding identifier within a generator head": {}, 168 | 169 | /** function* g() { (yield) => null } **/ 170 | "yield is not allowed within an arrow parameter list inside of a generator - 1": {}, 171 | 172 | /** function* g() { (x = yield 1) => null } **/ 173 | "yield is not allowed within an arrow parameter list inside of a generator - 2": {}, 174 | 175 | /** function* g() { yield, null } **/ 176 | "yield is allowed in comma expressions": 177 | { type: 'Script', 178 | start: 0, 179 | end: 29, 180 | statements: 181 | [ { type: 'FunctionDeclaration', 182 | start: 0, 183 | end: 29, 184 | kind: 'generator', 185 | identifier: 186 | { type: 'Identifier', 187 | start: 10, 188 | end: 11, 189 | value: 'g', 190 | context: 'declaration' }, 191 | params: [], 192 | body: 193 | { type: 'FunctionBody', 194 | start: 14, 195 | end: 29, 196 | statements: 197 | [ { type: 'ExpressionStatement', 198 | start: 16, 199 | end: 27, 200 | expression: 201 | { type: 'SequenceExpression', 202 | start: 16, 203 | end: 27, 204 | expressions: 205 | [ { type: 'YieldExpression', 206 | start: 16, 207 | end: 21, 208 | delegate: false, 209 | expression: null }, 210 | { type: 'NullLiteral', start: 23, end: 27 } ] } } ] } } ] }, 211 | 212 | /** function* g() { [yield] } **/ 213 | "yield is allowed as last element of array literal": 214 | { 215 | type: 'Script', 216 | start: 0, 217 | end: 25, 218 | statements: 219 | [ { 220 | type: 'FunctionDeclaration', 221 | start: 0, 222 | end: 25, 223 | kind: 'generator', 224 | identifier: 225 | { 226 | type: 'Identifier', 227 | start: 10, 228 | end: 11, 229 | value: 'g', 230 | context: 'declaration' }, 231 | params: [], 232 | body: 233 | { 234 | type: 'FunctionBody', 235 | start: 14, 236 | end: 25, 237 | statements: 238 | [ { 239 | type: 'ExpressionStatement', 240 | start: 16, 241 | end: 23, 242 | expression: 243 | { 244 | type: 'ArrayLiteral', 245 | start: 16, 246 | end: 23, 247 | elements: 248 | [ { 249 | type: 'YieldExpression', 250 | start: 17, 251 | end: 22, 252 | delegate: false, 253 | expression: null } ], 254 | trailingComma: false } } ] } } ] }, 255 | 256 | /** function* g() { for (let i = yield in x); } **/ 257 | "empty yield can appear before in in for-in": 258 | { 259 | type: 'Script', 260 | start: 0, 261 | end: 43, 262 | statements: 263 | [ { 264 | type: 'FunctionDeclaration', 265 | start: 0, 266 | end: 43, 267 | kind: 'generator', 268 | identifier: 269 | { 270 | type: 'Identifier', 271 | start: 10, 272 | end: 11, 273 | value: 'g', 274 | context: 'declaration' }, 275 | params: [], 276 | body: 277 | { 278 | type: 'FunctionBody', 279 | start: 14, 280 | end: 43, 281 | statements: 282 | [ { 283 | type: 'ForInStatement', 284 | start: 16, 285 | end: 41, 286 | left: 287 | { 288 | type: 'VariableDeclaration', 289 | start: 21, 290 | end: 34, 291 | kind: 'let', 292 | declarations: 293 | [ { 294 | type: 'VariableDeclarator', 295 | start: 25, 296 | end: 34, 297 | pattern: 298 | { 299 | type: 'Identifier', 300 | start: 25, 301 | end: 26, 302 | value: 'i', 303 | context: 'declaration' }, 304 | initializer: 305 | { 306 | type: 'YieldExpression', 307 | start: 29, 308 | end: 34, 309 | delegate: false, 310 | expression: null } } ], 311 | error: 'Invalid initializer in for-in statement' }, 312 | right: 313 | { 314 | type: 'Identifier', 315 | start: 38, 316 | end: 39, 317 | value: 'x', 318 | context: 'variable' }, 319 | body: { type: 'EmptyStatement', start: 40, end: 41 } } ] } } ] }, 320 | 321 | }) 322 | -------------------------------------------------------------------------------- /test/parse/modules/import.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** import "x"; **/ 4 | 'import not allowed in non-module': {}, 5 | 6 | /*** import { x } from "x"; ***/ 7 | 'import from a url': 8 | { type: 'Module', 9 | start: 0, 10 | end: 22, 11 | statements: 12 | [ { type: 'ImportDeclaration', 13 | start: 0, 14 | end: 22, 15 | imports: 16 | { type: 'NamedImports', 17 | start: 7, 18 | end: 12, 19 | specifiers: 20 | [ { type: 'ImportSpecifier', 21 | start: 9, 22 | end: 10, 23 | imported: 24 | { type: 'Identifier', 25 | start: 9, 26 | end: 10, 27 | value: 'x', 28 | context: 'declaration' }, 29 | local: null } ] }, 30 | from: { type: 'StringLiteral', start: 18, end: 21, value: 'x' } } ] }, 31 | 32 | /*** import { x as y } from "x"; ***/ 33 | 'renaming imported bindings': 34 | { type: 'Module', 35 | start: 0, 36 | end: 27, 37 | statements: 38 | [ { type: 'ImportDeclaration', 39 | start: 0, 40 | end: 27, 41 | imports: 42 | { type: 'NamedImports', 43 | start: 7, 44 | end: 17, 45 | specifiers: 46 | [ { type: 'ImportSpecifier', 47 | start: 9, 48 | end: 15, 49 | imported: { type: 'Identifier', start: 9, end: 10, value: 'x', context: '' }, 50 | local: 51 | { type: 'Identifier', 52 | start: 14, 53 | end: 15, 54 | value: 'y', 55 | context: 'declaration' } } ] }, 56 | from: { type: 'StringLiteral', start: 23, end: 26, value: 'x' } } ] }, 57 | 58 | /*** import {} from "x"; ***/ 59 | 'empty import specifier set': 60 | { type: 'Module', 61 | start: 0, 62 | end: 19, 63 | statements: 64 | [ { type: 'ImportDeclaration', 65 | start: 0, 66 | end: 19, 67 | imports: { type: 'NamedImports', start: 7, end: 9, specifiers: [] }, 68 | from: { type: 'StringLiteral', start: 15, end: 18, value: 'x' } } ] }, 69 | 70 | /*** import { x, } from "x"; ***/ 71 | 'import list may end with a comma': 72 | { type: 'Module', 73 | start: 0, 74 | end: 23, 75 | statements: 76 | [ { type: 'ImportDeclaration', 77 | start: 0, 78 | end: 23, 79 | imports: 80 | { type: 'NamedImports', 81 | start: 7, 82 | end: 13, 83 | specifiers: 84 | [ { type: 'ImportSpecifier', 85 | start: 9, 86 | end: 10, 87 | imported: 88 | { type: 'Identifier', 89 | start: 9, 90 | end: 10, 91 | value: 'x', 92 | context: 'declaration' }, 93 | local: null } ] }, 94 | from: { type: 'StringLiteral', start: 19, end: 22, value: 'x' } } ] }, 95 | 96 | /*** import { , } from "x"; ***/ 97 | 'import list cannot contain only a comma': {}, 98 | 99 | /*** import { default as y } from "x"; ***/ 100 | 'import a keyword-named binding': 101 | { type: 'Module', 102 | start: 0, 103 | end: 33, 104 | statements: 105 | [ { type: 'ImportDeclaration', 106 | start: 0, 107 | end: 33, 108 | imports: 109 | { type: 'NamedImports', 110 | start: 7, 111 | end: 23, 112 | specifiers: 113 | [ { type: 'ImportSpecifier', 114 | start: 9, 115 | end: 21, 116 | imported: 117 | { type: 'Identifier', 118 | start: 9, 119 | end: 16, 120 | value: 'default', 121 | context: '' }, 122 | local: 123 | { type: 'Identifier', 124 | start: 20, 125 | end: 21, 126 | value: 'y', 127 | context: 'declaration' } } ] }, 128 | from: { type: 'StringLiteral', start: 29, end: 32, value: 'x' } } ] }, 129 | 130 | /*** import { default } from "x"; ***/ 131 | 'importing of non-identifier bindings is not allowed': {}, 132 | 133 | /*** import "x"; ***/ 134 | 'import declaration without a specifier list': 135 | { type: 'Module', 136 | start: 0, 137 | end: 11, 138 | statements: 139 | [ { type: 'ImportDeclaration', 140 | start: 0, 141 | end: 11, 142 | imports: null, 143 | from: { type: 'StringLiteral', start: 7, end: 10, value: 'x' } } ] }, 144 | 145 | /*** import * as x from "x.js"; ***/ 146 | 'importing the module namespace': 147 | { type: 'Module', 148 | start: 0, 149 | end: 26, 150 | statements: 151 | [ { type: 'ImportDeclaration', 152 | start: 0, 153 | end: 26, 154 | imports: 155 | { type: 'NamespaceImport', 156 | start: 7, 157 | end: 13, 158 | identifier: 159 | { type: 'Identifier', 160 | start: 12, 161 | end: 13, 162 | value: 'x', 163 | context: 'declaration' } }, 164 | from: { type: 'StringLiteral', start: 19, end: 25, value: 'x.js' } } ] }, 165 | 166 | /*** import x, * as y from "a"; ***/ 167 | 'importing a default and namespace': 168 | { type: 'Module', 169 | start: 0, 170 | end: 26, 171 | statements: 172 | [ { type: 'ImportDeclaration', 173 | start: 0, 174 | end: 26, 175 | imports: 176 | { type: 'DefaultImport', 177 | start: 7, 178 | end: 16, 179 | identifier: 180 | { type: 'Identifier', 181 | start: 7, 182 | end: 8, 183 | value: 'x', 184 | context: 'declaration' }, 185 | imports: 186 | { type: 'NamespaceImport', 187 | start: 10, 188 | end: 16, 189 | identifier: 190 | { type: 'Identifier', 191 | start: 15, 192 | end: 16, 193 | value: 'y', 194 | context: 'declaration' } } }, 195 | from: { type: 'StringLiteral', start: 22, end: 25, value: 'a' } } ] }, 196 | 197 | /*** import x, { y } from "a"; ***/ 198 | 'importing a default and named imports': 199 | { type: 'Module', 200 | start: 0, 201 | end: 25, 202 | statements: 203 | [ { type: 'ImportDeclaration', 204 | start: 0, 205 | end: 25, 206 | imports: 207 | { type: 'DefaultImport', 208 | start: 7, 209 | end: 15, 210 | identifier: 211 | { type: 'Identifier', 212 | start: 7, 213 | end: 8, 214 | value: 'x', 215 | context: 'declaration' }, 216 | imports: 217 | { type: 'NamedImports', 218 | start: 10, 219 | end: 15, 220 | specifiers: 221 | [ { type: 'ImportSpecifier', 222 | start: 12, 223 | end: 13, 224 | imported: 225 | { type: 'Identifier', 226 | start: 12, 227 | end: 13, 228 | value: 'y', 229 | context: 'declaration' }, 230 | local: null } ] } }, 231 | from: { type: 'StringLiteral', start: 21, end: 24, value: 'a' } } ] }, 232 | 233 | /*** 1; import 'x'; 2; ***/ 234 | 'import declarations allowed between statements': { type: 'Module', 235 | start: 0, 236 | end: 17, 237 | statements: 238 | [ { type: 'ExpressionStatement', 239 | start: 0, 240 | end: 2, 241 | expression: { type: 'NumberLiteral', start: 0, end: 1, value: 1 } }, 242 | { type: 'ImportDeclaration', 243 | start: 3, 244 | end: 14, 245 | imports: null, 246 | from: { type: 'StringLiteral', start: 10, end: 13, value: 'x' } }, 247 | { type: 'ExpressionStatement', 248 | start: 15, 249 | end: 17, 250 | expression: { type: 'NumberLiteral', start: 15, end: 16, value: 2 } } ] }, 251 | 252 | /*** await (1); ***/ 253 | 'await is reserved within modules - 1': {}, 254 | 255 | /*** { await (1); } ***/ 256 | 'await is reserved within modules - 2': {}, 257 | 258 | /** await (1) **/ 259 | 'await is not reserved in scripts': 260 | { type: 'Script', 261 | start: 0, 262 | end: 9, 263 | statements: 264 | [ { type: 'ExpressionStatement', 265 | start: 0, 266 | end: 9, 267 | expression: 268 | { type: 'CallExpression', 269 | start: 0, 270 | end: 9, 271 | callee: 272 | { type: 'Identifier', 273 | start: 0, 274 | end: 5, 275 | value: 'await', 276 | context: 'variable' }, 277 | arguments: [ { type: 'NumberLiteral', start: 7, end: 8, value: 1 } ], 278 | trailingComma: false } } ] }, 279 | 280 | /*** import.meta ***/ 281 | 'import.meta meta property': 282 | { type: 'Module', 283 | start: 0, 284 | end: 11, 285 | statements: 286 | [ { type: 'ExpressionStatement', 287 | start: 0, 288 | end: 11, 289 | expression: 290 | { type: 'MetaProperty', 291 | start: 0, 292 | end: 11, 293 | left: 'import', 294 | right: 'meta' } } ] }, 295 | 296 | /** import.meta **/ 297 | 'import.meta only allowed in modules': 298 | {}, 299 | 300 | /*** import('foobar') ***/ 301 | 'dynamic import': 302 | { 303 | type: 'Module', 304 | start: 0, 305 | end: 16, 306 | statements: 307 | [ { 308 | type: 'ExpressionStatement', 309 | start: 0, 310 | end: 16, 311 | expression: 312 | { 313 | type: 'ImportCall', 314 | argument: { type: 'StringLiteral', start: 7, end: 15, value: 'foobar' }, 315 | start: 0, 316 | end: 16 } } ] }, 317 | 318 | /** import('foobar') **/ 319 | 'dynamic import allowed in script': 320 | { 321 | type: 'Script', 322 | start: 0, 323 | end: 16, 324 | statements: 325 | [ { 326 | type: 'ExpressionStatement', 327 | start: 0, 328 | end: 16, 329 | expression: 330 | { 331 | type: 'ImportCall', 332 | argument: { type: 'StringLiteral', start: 7, end: 15, value: 'foobar' }, 333 | start: 0, 334 | end: 16 } } ] }, 335 | 336 | }) 337 | -------------------------------------------------------------------------------- /test/parse/destructuring/object-assignment.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** ({ x: x } = a); **/ 4 | 'basic destructuring': 5 | { type: 'Script', 6 | start: 0, 7 | end: 15, 8 | statements: 9 | [ { type: 'ExpressionStatement', 10 | start: 0, 11 | end: 15, 12 | expression: 13 | { type: 'ParenExpression', 14 | start: 0, 15 | end: 14, 16 | expression: 17 | { type: 'AssignmentExpression', 18 | start: 1, 19 | end: 13, 20 | operator: '=', 21 | left: 22 | { type: 'ObjectPattern', 23 | start: 1, 24 | end: 9, 25 | properties: 26 | [ { type: 'PatternProperty', 27 | start: 3, 28 | end: 7, 29 | name: { type: 'Identifier', start: 3, end: 4, value: 'x', context: '' }, 30 | pattern: 31 | { type: 'Identifier', 32 | start: 6, 33 | end: 7, 34 | value: 'x', 35 | context: 'variable' }, 36 | initializer: null } ], 37 | trailingComma: false }, 38 | right: 39 | { type: 'Identifier', 40 | start: 12, 41 | end: 13, 42 | value: 'a', 43 | context: 'variable' } } } } ] }, 44 | 45 | /** ({ x } = a); **/ 46 | 'key shorthands': 47 | { type: 'Script', 48 | start: 0, 49 | end: 12, 50 | statements: 51 | [ { type: 'ExpressionStatement', 52 | start: 0, 53 | end: 12, 54 | expression: 55 | { type: 'ParenExpression', 56 | start: 0, 57 | end: 11, 58 | expression: 59 | { type: 'AssignmentExpression', 60 | start: 1, 61 | end: 10, 62 | operator: '=', 63 | left: 64 | { type: 'ObjectPattern', 65 | start: 1, 66 | end: 6, 67 | properties: 68 | [ { type: 'PatternProperty', 69 | start: 3, 70 | end: 4, 71 | name: 72 | { type: 'Identifier', 73 | start: 3, 74 | end: 4, 75 | value: 'x', 76 | context: 'variable' }, 77 | pattern: null, 78 | initializer: null } ], 79 | trailingComma: false }, 80 | right: 81 | { type: 'Identifier', 82 | start: 9, 83 | end: 10, 84 | value: 'a', 85 | context: 'variable' } } } } ] }, 86 | 87 | /** ({ x = 123 } = a); **/ 88 | 'defaults are allowed': 89 | { type: 'Script', 90 | start: 0, 91 | end: 18, 92 | statements: 93 | [ { type: 'ExpressionStatement', 94 | start: 0, 95 | end: 18, 96 | expression: 97 | { type: 'ParenExpression', 98 | start: 0, 99 | end: 17, 100 | expression: 101 | { type: 'AssignmentExpression', 102 | start: 1, 103 | end: 16, 104 | operator: '=', 105 | left: 106 | { type: 'ObjectPattern', 107 | start: 1, 108 | end: 12, 109 | properties: 110 | [ { type: 'PatternProperty', 111 | start: 3, 112 | end: 10, 113 | name: 114 | { type: 'Identifier', 115 | start: 3, 116 | end: 4, 117 | value: 'x', 118 | context: 'variable' }, 119 | pattern: null, 120 | initializer: { type: 'NumberLiteral', start: 7, end: 10, value: 123 }, 121 | error: '' } ], 122 | trailingComma: false }, 123 | right: 124 | { type: 'Identifier', 125 | start: 15, 126 | end: 16, 127 | value: 'a', 128 | context: 'variable' } } } } ] }, 129 | 130 | /** ({ x: x }) = a; **/ 131 | 'parens are not allowed around pattern': {}, 132 | 133 | /** ({ x = 123 }); **/ 134 | 'invalid object literals throw': {}, 135 | 136 | /** ({ if } = a); **/ 137 | 'keywords cannot be used as simple names': {}, 138 | 139 | /** "use strict"; ({ args: arguments } = a); **/ 140 | 'assignment to arguments throws in strict mode': {}, 141 | 142 | /** "use strict"; ({ arguments } = a); **/ 143 | 'shorthand assignment to arguments throws in strict mode': {}, 144 | 145 | /** "use strict"; ({ x: a, x: b } = q); **/ 146 | 'duplicate names do not throw': 147 | { type: 'Script', 148 | start: 0, 149 | end: 35, 150 | statements: 151 | [ { type: 'Directive', 152 | start: 0, 153 | end: 13, 154 | value: 'use strict', 155 | expression: { type: 'StringLiteral', start: 0, end: 12, value: 'use strict' } }, 156 | { type: 'ExpressionStatement', 157 | start: 14, 158 | end: 35, 159 | expression: 160 | { type: 'ParenExpression', 161 | start: 14, 162 | end: 34, 163 | expression: 164 | { type: 'AssignmentExpression', 165 | start: 15, 166 | end: 33, 167 | operator: '=', 168 | left: 169 | { type: 'ObjectPattern', 170 | start: 15, 171 | end: 29, 172 | properties: 173 | [ { type: 'PatternProperty', 174 | start: 17, 175 | end: 21, 176 | name: { type: 'Identifier', start: 17, end: 18, value: 'x', context: '' }, 177 | pattern: 178 | { type: 'Identifier', 179 | start: 20, 180 | end: 21, 181 | value: 'a', 182 | context: 'variable' }, 183 | initializer: null }, 184 | { type: 'PatternProperty', 185 | start: 23, 186 | end: 27, 187 | name: { type: 'Identifier', start: 23, end: 24, value: 'x', context: '' }, 188 | pattern: 189 | { type: 'Identifier', 190 | start: 26, 191 | end: 27, 192 | value: 'b', 193 | context: 'variable' }, 194 | initializer: null } ], 195 | trailingComma: false }, 196 | right: 197 | { type: 'Identifier', 198 | start: 32, 199 | end: 33, 200 | value: 'q', 201 | context: 'variable' } } } } ] }, 202 | 203 | /** ({ x: y.z } = a) **/ 204 | 'assignment target can be a member expression': 205 | { type: 'Script', 206 | start: 0, 207 | end: 16, 208 | statements: 209 | [ { type: 'ExpressionStatement', 210 | start: 0, 211 | end: 16, 212 | expression: 213 | { type: 'ParenExpression', 214 | start: 0, 215 | end: 16, 216 | expression: 217 | { type: 'AssignmentExpression', 218 | start: 1, 219 | end: 15, 220 | operator: '=', 221 | left: 222 | { type: 'ObjectPattern', 223 | start: 1, 224 | end: 11, 225 | properties: 226 | [ { type: 'PatternProperty', 227 | start: 3, 228 | end: 9, 229 | name: { type: 'Identifier', start: 3, end: 4, value: 'x', context: '' }, 230 | pattern: 231 | { type: 'MemberExpression', 232 | start: 6, 233 | end: 9, 234 | object: 235 | { type: 'Identifier', 236 | start: 6, 237 | end: 7, 238 | value: 'y', 239 | context: 'variable' }, 240 | property: { type: 'Identifier', start: 8, end: 9, value: 'z', context: '' } }, 241 | initializer: null } ], 242 | trailingComma: false }, 243 | right: 244 | { type: 'Identifier', 245 | start: 14, 246 | end: 15, 247 | value: 'a', 248 | context: 'variable' } } } } ] }, 249 | 250 | /** ({ x: (y) } = a); **/ 251 | 'simple targets are unwrapped': 252 | { 253 | type: 'Script', 254 | start: 0, 255 | end: 17, 256 | statements: 257 | [ { 258 | type: 'ExpressionStatement', 259 | start: 0, 260 | end: 17, 261 | expression: 262 | { 263 | type: 'ParenExpression', 264 | start: 0, 265 | end: 16, 266 | expression: 267 | { 268 | type: 'AssignmentExpression', 269 | start: 1, 270 | end: 15, 271 | operator: '=', 272 | left: 273 | { 274 | type: 'ObjectPattern', 275 | start: 1, 276 | end: 11, 277 | properties: 278 | [ { 279 | type: 'PatternProperty', 280 | start: 3, 281 | end: 9, 282 | name: { type: 'Identifier', start: 3, end: 4, value: 'x', context: '' }, 283 | pattern: 284 | { 285 | type: 'ParenExpression', 286 | start: 6, 287 | end: 9, 288 | expression: 289 | { 290 | type: 'Identifier', 291 | start: 7, 292 | end: 8, 293 | value: 'y', 294 | context: 'variable' } }, 295 | initializer: null } ], 296 | trailingComma: false }, 297 | right: 298 | { 299 | type: 'Identifier', 300 | start: 14, 301 | end: 15, 302 | value: 'a', 303 | context: 'variable' } } } } ] }, 304 | 305 | /** ({ x: f() } = a); **/ 306 | 'call expressions are invalid assignment targets': {}, 307 | 308 | /** ({ x: new f } = a); **/ 309 | 'new expressions are invalid assignment targets': {}, 310 | 311 | }) 312 | -------------------------------------------------------------------------------- /test/parse/modules/export.js: -------------------------------------------------------------------------------- 1 | ({ 2 | 3 | /** export var x; **/ 4 | 'export not allowed outside of module': {}, 5 | 6 | /*** export function f() {} ***/ 7 | 'export a function': 8 | { type: 'Module', 9 | start: 0, 10 | end: 22, 11 | statements: 12 | [ { type: 'ExportDeclaration', 13 | start: 0, 14 | end: 22, 15 | declaration: 16 | { type: 'FunctionDeclaration', 17 | start: 7, 18 | end: 22, 19 | kind: '', 20 | identifier: 21 | { type: 'Identifier', 22 | start: 16, 23 | end: 17, 24 | value: 'f', 25 | context: 'declaration' }, 26 | params: [], 27 | body: { type: 'FunctionBody', start: 20, end: 22, statements: [] } } } ] }, 28 | 29 | /*** export class C {} ***/ 30 | 'export a class': 31 | { type: 'Module', 32 | start: 0, 33 | end: 17, 34 | statements: 35 | [ { type: 'ExportDeclaration', 36 | start: 0, 37 | end: 17, 38 | declaration: 39 | { type: 'ClassDeclaration', 40 | start: 7, 41 | end: 17, 42 | identifier: 43 | { type: 'Identifier', 44 | start: 13, 45 | end: 14, 46 | value: 'C', 47 | context: 'declaration' }, 48 | base: null, 49 | body: { type: 'ClassBody', start: 15, end: 17, elements: [] } } } ] }, 50 | 51 | /*** export var x; ***/ 52 | 'export a var': 53 | { type: 'Module', 54 | start: 0, 55 | end: 13, 56 | statements: 57 | [ { type: 'ExportDeclaration', 58 | start: 0, 59 | end: 13, 60 | declaration: 61 | { type: 'VariableDeclaration', 62 | start: 7, 63 | end: 13, 64 | kind: 'var', 65 | declarations: 66 | [ { type: 'VariableDeclarator', 67 | start: 11, 68 | end: 12, 69 | pattern: 70 | { type: 'Identifier', 71 | start: 11, 72 | end: 12, 73 | value: 'x', 74 | context: 'declaration' }, 75 | initializer: null } ] } } ] }, 76 | 77 | /*** export let x = 123; ***/ 78 | 'export a let declaration': 79 | { type: 'Module', 80 | start: 0, 81 | end: 19, 82 | statements: 83 | [ { type: 'ExportDeclaration', 84 | start: 0, 85 | end: 19, 86 | declaration: 87 | { type: 'VariableDeclaration', 88 | start: 7, 89 | end: 19, 90 | kind: 'let', 91 | declarations: 92 | [ { type: 'VariableDeclarator', 93 | start: 11, 94 | end: 18, 95 | pattern: 96 | { type: 'Identifier', 97 | start: 11, 98 | end: 12, 99 | value: 'x', 100 | context: 'declaration' }, 101 | initializer: { type: 'NumberLiteral', start: 15, end: 18, value: 123 } } ] } } ] }, 102 | 103 | /*** export const x = 123; ***/ 104 | 'export a const declaration': 105 | { type: 'Module', 106 | start: 0, 107 | end: 21, 108 | statements: 109 | [ { type: 'ExportDeclaration', 110 | start: 0, 111 | end: 21, 112 | declaration: 113 | { type: 'VariableDeclaration', 114 | start: 7, 115 | end: 21, 116 | kind: 'const', 117 | declarations: 118 | [ { type: 'VariableDeclarator', 119 | start: 13, 120 | end: 20, 121 | pattern: 122 | { type: 'Identifier', 123 | start: 13, 124 | end: 14, 125 | value: 'x', 126 | context: 'declaration' }, 127 | initializer: { type: 'NumberLiteral', start: 17, end: 20, value: 123 } } ] } } ] }, 128 | 129 | /*** export { x }; ***/ 130 | 'export an identifier': 131 | { type: 'Module', 132 | start: 0, 133 | end: 13, 134 | statements: 135 | [ { type: 'ExportNameList', 136 | start: 0, 137 | end: 13, 138 | specifiers: 139 | [ { type: 'ExportSpecifier', 140 | start: 9, 141 | end: 10, 142 | local: { type: 'Identifier', start: 9, end: 10, value: 'x', context: 'variable' }, 143 | exported: null } ], 144 | from: null } ] }, 145 | 146 | /*** export { x, y }; ***/ 147 | 'export multiple identifiers': 148 | { type: 'Module', 149 | start: 0, 150 | end: 16, 151 | statements: 152 | [ { type: 'ExportNameList', 153 | start: 0, 154 | end: 16, 155 | specifiers: 156 | [ { type: 'ExportSpecifier', 157 | start: 9, 158 | end: 10, 159 | local: { type: 'Identifier', start: 9, end: 10, value: 'x', context: 'variable' }, 160 | exported: null }, 161 | { type: 'ExportSpecifier', 162 | start: 12, 163 | end: 13, 164 | local: { type: 'Identifier', start: 12, end: 13, value: 'y', context: 'variable' }, 165 | exported: null } ], 166 | from: null } ] }, 167 | 168 | /*** export { x as y }; ***/ 169 | 'export and rename identifier': 170 | { type: 'Module', 171 | start: 0, 172 | end: 18, 173 | statements: 174 | [ { type: 'ExportNameList', 175 | start: 0, 176 | end: 18, 177 | specifiers: 178 | [ { type: 'ExportSpecifier', 179 | start: 9, 180 | end: 15, 181 | local: { type: 'Identifier', start: 9, end: 10, value: 'x', context: 'variable' }, 182 | exported: { type: 'Identifier', start: 14, end: 15, value: 'y', context: '' } } ], 183 | from: null } ] }, 184 | 185 | /*** export { x as default }; ***/ 186 | 'exporting a default binding': 187 | { type: 'Module', 188 | start: 0, 189 | end: 24, 190 | statements: 191 | [ { type: 'ExportNameList', 192 | start: 0, 193 | end: 24, 194 | specifiers: 195 | [ { type: 'ExportSpecifier', 196 | start: 9, 197 | end: 21, 198 | local: { type: 'Identifier', start: 9, end: 10, value: 'x', context: 'variable' }, 199 | exported: 200 | { type: 'Identifier', 201 | start: 14, 202 | end: 21, 203 | value: 'default', 204 | context: '' } } ], 205 | from: null } ] }, 206 | 207 | /*** export { x as y } from "x"; ***/ 208 | 'exporting a named set from an external module': 209 | { type: 'Module', 210 | start: 0, 211 | end: 27, 212 | statements: 213 | [ { type: 'ExportNameList', 214 | start: 0, 215 | end: 27, 216 | specifiers: 217 | [ { type: 'ExportSpecifier', 218 | start: 9, 219 | end: 15, 220 | local: { type: 'Identifier', start: 9, end: 10, value: 'x', context: '' }, 221 | exported: { type: 'Identifier', start: 14, end: 15, value: 'y', context: '' } } ], 222 | from: { type: 'StringLiteral', start: 23, end: 26, value: 'x' } } ] }, 223 | 224 | /*** export * from "x"; ***/ 225 | 'exporting everything from an external module': 226 | { type: 'Module', 227 | start: 0, 228 | end: 18, 229 | statements: 230 | [ { type: 'ExportNamespace', 231 | start: 0, 232 | end: 18, 233 | identifier: null, 234 | from: { type: 'StringLiteral', start: 14, end: 17, value: 'x' } } ] }, 235 | 236 | /*** export *; ***/ 237 | 'exporting everything must include a specifier': {}, 238 | 239 | /*** export { if }; ***/ 240 | 'local export does not allow identifier names': {}, 241 | 242 | /*** export { implements }; ***/ 243 | 'local export does not allow strict reserved words': {}, 244 | 245 | /*** export { if } from "x"; ***/ 246 | 'export from should allow identifier names': 247 | { type: 'Module', 248 | start: 0, 249 | end: 23, 250 | statements: 251 | [ { type: 'ExportNameList', 252 | start: 0, 253 | end: 23, 254 | specifiers: 255 | [ { type: 'ExportSpecifier', 256 | start: 9, 257 | end: 11, 258 | local: { type: 'Identifier', start: 9, end: 11, value: 'if', context: '' }, 259 | exported: null } ], 260 | from: { type: 'StringLiteral', start: 19, end: 22, value: 'x' } } ] }, 261 | 262 | /*** export default class C {} ***/ 263 | 'export default class declaration': 264 | { type: 'Module', 265 | start: 0, 266 | end: 25, 267 | statements: 268 | [ { type: 'ExportDefault', 269 | binding: 270 | { type: 'ClassDeclaration', 271 | start: 15, 272 | end: 25, 273 | identifier: 274 | { type: 'Identifier', 275 | start: 21, 276 | end: 22, 277 | value: 'C', 278 | context: 'declaration' }, 279 | base: null, 280 | body: { type: 'ClassBody', start: 23, end: 25, elements: [] } }, 281 | start: 0, 282 | end: 25 } ] }, 283 | 284 | /*** export default class {} ***/ 285 | 'export default class with no identifier': 286 | { type: 'Module', 287 | start: 0, 288 | end: 23, 289 | statements: 290 | [ { type: 'ExportDefault', 291 | binding: 292 | { type: 'ClassDeclaration', 293 | start: 15, 294 | end: 23, 295 | identifier: null, 296 | base: null, 297 | body: { type: 'ClassBody', start: 21, end: 23, elements: [] } }, 298 | start: 0, 299 | end: 23 } ] }, 300 | 301 | /*** export default function F() {} ***/ 302 | 'export default function declaration': 303 | { type: 'Module', 304 | start: 0, 305 | end: 30, 306 | statements: 307 | [ { type: 'ExportDefault', 308 | binding: 309 | { type: 'FunctionDeclaration', 310 | start: 15, 311 | end: 30, 312 | kind: '', 313 | identifier: 314 | { type: 'Identifier', 315 | start: 24, 316 | end: 25, 317 | value: 'F', 318 | context: 'declaration' }, 319 | params: [], 320 | body: { type: 'FunctionBody', start: 28, end: 30, statements: [] } }, 321 | start: 0, 322 | end: 30 } ] }, 323 | 324 | /*** export default function() {} ***/ 325 | 'export default function with no identifier': 326 | { type: 'Module', 327 | start: 0, 328 | end: 28, 329 | statements: 330 | [ { type: 'ExportDefault', 331 | binding: 332 | { type: 'FunctionDeclaration', 333 | start: 15, 334 | end: 28, 335 | kind: '', 336 | identifier: null, 337 | params: [], 338 | body: { type: 'FunctionBody', start: 26, end: 28, statements: [] } }, 339 | start: 0, 340 | end: 28 } ] }, 341 | 342 | /*** export default 1 + 1; ***/ 343 | 'export default assignment expression': 344 | { type: 'Module', 345 | start: 0, 346 | end: 21, 347 | statements: 348 | [ { type: 'ExportDefault', 349 | binding: 350 | { type: 'BinaryExpression', 351 | start: 15, 352 | end: 20, 353 | operator: '+', 354 | left: { type: 'NumberLiteral', start: 15, end: 16, value: 1 }, 355 | right: { type: 'NumberLiteral', start: 19, end: 20, value: 1 } }, 356 | start: 0, 357 | end: 21 } ] }, 358 | 359 | /*** export x from "y"; ***/ 360 | 'export a default from another module': 361 | { type: 'Module', 362 | start: 0, 363 | end: 18, 364 | statements: 365 | [ { type: 'ExportDefaultFrom', 366 | start: 0, 367 | end: 18, 368 | identifier: { type: 'Identifier', start: 7, end: 8, value: 'x', context: '' }, 369 | from: { type: 'StringLiteral', start: 14, end: 17, value: 'y' } } ] }, 370 | 371 | /*** export * as x from "y"; ***/ 372 | 'export a namespace from another module': 373 | { type: 'Module', 374 | start: 0, 375 | end: 23, 376 | statements: 377 | [ { type: 'ExportNamespace', 378 | start: 0, 379 | end: 23, 380 | identifier: 381 | { type: 'Identifier', 382 | start: 12, 383 | end: 13, 384 | value: 'x', 385 | context: 'declaration' }, 386 | from: { type: 'StringLiteral', start: 19, end: 22, value: 'y' } } ] }, 387 | 388 | }) 389 | --------------------------------------------------------------------------------