From 29a27b069e2a7b7ad74e198288947afb88990669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20K=C3=BChner?= Date: Wed, 3 Nov 2021 20:28:10 +0100 Subject: [PATCH] support for es2022 class propertydefinitions and private members -> see https://github.com/estree/estree/blob/master/es2022.md --- src/esprima.ts | 12 +- src/nodes.ts | 1770 +++++++++++++++++++++++----------------------- src/parser.ts | 178 +++-- src/scanner.ts | 1 + src/syntax.ts | 1 + src/tokenizer.ts | 2 +- 6 files changed, 1022 insertions(+), 942 deletions(-) diff --git a/src/esprima.ts b/src/esprima.ts index 0c18445cb..b22b82313 100644 --- a/src/esprima.ts +++ b/src/esprima.ts @@ -24,10 +24,12 @@ import { CommentHandler } from './comment-handler'; import { JSXParser } from './jsx-parser'; -import { Parser } from './parser'; +import { Config, Parser } from './parser'; import { Tokenizer } from './tokenizer'; -export function parse(code: string, options, delegate) { +export { Config } from './parser'; + +export function parse(code: string, options?: Config, delegate?) { let commentHandler: CommentHandler | null = null; const proxyDelegate = (node, metadata) => { if (delegate) { @@ -79,19 +81,19 @@ export function parse(code: string, options, delegate) { return ast; } -export function parseModule(code: string, options, delegate) { +export function parseModule(code: string, options?: Config, delegate?) { const parsingOptions = options || {}; parsingOptions.sourceType = 'module'; return parse(code, parsingOptions, delegate); } -export function parseScript(code: string, options, delegate) { +export function parseScript(code: string, options?: Config, delegate?) { const parsingOptions = options || {}; parsingOptions.sourceType = 'script'; return parse(code, parsingOptions, delegate); } -export function tokenize(code: string, options, delegate) { +export function tokenize(code: string, options?: Config, delegate?) { const tokenizer = new Tokenizer(code, options); const tokens: any = []; diff --git a/src/nodes.ts b/src/nodes.ts index 9270ff630..a7310b1d4 100644 --- a/src/nodes.ts +++ b/src/nodes.ts @@ -1,892 +1,878 @@ -import { Syntax } from './syntax'; - -export type ArgumentListElement = Expression | SpreadElement; -export type ArrayExpressionElement = Expression | SpreadElement | null; -export type ArrayPatternElement = AssignmentPattern | BindingIdentifier | BindingPattern | RestElement | null; -export type BindingPattern = ArrayPattern | ObjectPattern; -export type BindingIdentifier = Identifier; -export type ChainElement = CallExpression | ComputedMemberExpression | StaticMemberExpression; -export type Declaration = AsyncFunctionDeclaration | ClassDeclaration | ExportDeclaration | FunctionDeclaration | ImportDeclaration | VariableDeclaration; -export type ExportableDefaultDeclaration = BindingIdentifier | BindingPattern | ClassDeclaration | Expression | FunctionDeclaration; -export type ExportableNamedDeclaration = AsyncFunctionDeclaration | ClassDeclaration | FunctionDeclaration | VariableDeclaration; -export type ExportDeclaration = ExportAllDeclaration | ExportDefaultDeclaration | ExportNamedDeclaration; -export type Expression = ArrayExpression | ArrowFunctionExpression | AssignmentExpression | AsyncArrowFunctionExpression | AsyncFunctionExpression | - AwaitExpression | BinaryExpression | CallExpression | ChainExpression | ClassExpression | ComputedMemberExpression | - ConditionalExpression | Identifier | FunctionExpression | Literal | NewExpression | ObjectExpression | - RegexLiteral | SequenceExpression | StaticMemberExpression | TaggedTemplateExpression | - ThisExpression | UnaryExpression | UpdateExpression | YieldExpression; -export type FunctionParameter = AssignmentPattern | BindingIdentifier | BindingPattern; -export type ImportDeclarationSpecifier = ImportDefaultSpecifier | ImportNamespaceSpecifier | ImportSpecifier; -export type ObjectExpressionProperty = Property | SpreadElement; -export type ObjectPatternProperty = Property | RestElement; -export type Statement = AsyncFunctionDeclaration | BreakStatement | ContinueStatement | DebuggerStatement | DoWhileStatement | - EmptyStatement | ExpressionStatement | Directive | ForStatement | ForInStatement | ForOfStatement | - FunctionDeclaration | IfStatement | ReturnStatement | SwitchStatement | ThrowStatement | - TryStatement | VariableDeclaration | WhileStatement | WithStatement; -export type PropertyKey = Identifier | Literal; -export type PropertyValue = AssignmentPattern | AsyncFunctionExpression | BindingIdentifier | BindingPattern | FunctionExpression; -export type StatementListItem = Declaration | Statement; - -export class ArrayExpression { - readonly type: string; - readonly elements: ArrayExpressionElement[]; - constructor(elements: ArrayExpressionElement[]) { - this.type = Syntax.ArrayExpression; - this.elements = elements; - } -} - -export class ArrayPattern { - readonly type: string; - readonly elements: ArrayPatternElement[]; - constructor(elements: ArrayPatternElement[]) { - this.type = Syntax.ArrayPattern; - this.elements = elements; - } -} - -export class ArrowFunctionExpression { - readonly type: string; - readonly id: Identifier | null; - readonly params: FunctionParameter[]; - readonly body: BlockStatement | Expression; - readonly generator: boolean; - readonly expression: boolean; - readonly async: boolean; - constructor(params: FunctionParameter[], body: BlockStatement | Expression, expression: boolean) { - this.type = Syntax.ArrowFunctionExpression; - this.id = null; - this.params = params; - this.body = body; - this.generator = false; - this.expression = expression; - this.async = false; - } -} - -export class AssignmentExpression { - readonly type: string; - readonly operator: string; - readonly left: Expression; - readonly right: Expression; - constructor(operator: string, left: Expression, right: Expression) { - this.type = Syntax.AssignmentExpression; - this.operator = operator; - this.left = left; - this.right = right; - } -} - -export class AssignmentPattern { - readonly type: string; - readonly left: BindingIdentifier | BindingPattern; - readonly right: Expression; - constructor(left: BindingIdentifier | BindingPattern, right: Expression) { - this.type = Syntax.AssignmentPattern; - this.left = left; - this.right = right; - } -} - -export class AsyncArrowFunctionExpression { - readonly type: string; - readonly id: Identifier | null; - readonly params: FunctionParameter[]; - readonly body: BlockStatement | Expression; - readonly generator: boolean; - readonly expression: boolean; - readonly async: boolean; - constructor(params: FunctionParameter[], body: BlockStatement | Expression, expression: boolean) { - this.type = Syntax.ArrowFunctionExpression; - this.id = null; - this.params = params; - this.body = body; - this.generator = false; - this.expression = expression; - this.async = true; - } -} - -export class AsyncFunctionDeclaration { - readonly type: string; - readonly id: Identifier | null; - readonly params: FunctionParameter[]; - readonly body: BlockStatement; - readonly generator: boolean; - readonly expression: boolean; - readonly async: boolean; - constructor(id: Identifier | null, params: FunctionParameter[], body: BlockStatement, generator: boolean) { - this.type = Syntax.FunctionDeclaration; - this.id = id; - this.params = params; - this.body = body; - this.generator = generator; - this.expression = false; - this.async = true; - } -} - -export class AsyncFunctionExpression { - readonly type: string; - readonly id: Identifier | null; - readonly params: FunctionParameter[]; - readonly body: BlockStatement; - readonly generator: boolean; - readonly expression: boolean; - readonly async: boolean; - constructor(id: Identifier | null, params: FunctionParameter[], body: BlockStatement, generator: boolean) { - this.type = Syntax.FunctionExpression; - this.id = id; - this.params = params; - this.body = body; - this.generator = generator; - this.expression = false; - this.async = true; - } -} - -export class AwaitExpression { - readonly type: string; - readonly argument: Expression; - constructor(argument: Expression) { - this.type = Syntax.AwaitExpression; - this.argument = argument; - } -} - -export class BinaryExpression { - readonly type: string; - readonly operator: string; - readonly left: Expression; - readonly right: Expression; - constructor(operator: string, left: Expression, right: Expression) { - const logical = (operator === '||' || operator === '&&' || operator === '??'); - this.type = logical ? Syntax.LogicalExpression : Syntax.BinaryExpression; - this.operator = operator; - this.left = left; - this.right = right; - } -} - -export class BlockStatement { - readonly type: string; - readonly body: Statement[]; - constructor(body) { - this.type = Syntax.BlockStatement; - this.body = body; - } -} - -export class BreakStatement { - readonly type: string; - readonly label: Identifier | null; - constructor(label: Identifier | null) { - this.type = Syntax.BreakStatement; - this.label = label; - } -} - -export class CallExpression { - readonly type: string; - readonly callee: Expression | Import; - readonly arguments: ArgumentListElement[]; - readonly optional: boolean; - constructor(callee: Expression | Import, args: ArgumentListElement[], optional: boolean) { - this.type = Syntax.CallExpression; - this.callee = callee; - this.arguments = args; - this.optional = optional; - } -} - -export class CatchClause { - readonly type: string; - readonly param: BindingIdentifier | BindingPattern | null; - readonly body: BlockStatement; - constructor(param: BindingIdentifier | BindingPattern | null, body: BlockStatement) { - this.type = Syntax.CatchClause; - this.param = param; - this.body = body; - } -} - -export class ChainExpression { - readonly type: string; - readonly expression: ChainElement; - constructor(expression: ChainElement) { - this.type = Syntax.ChainExpression; - this.expression = expression; - } -} - -export class ClassBody { - readonly type: string; - readonly body: Property[]; - constructor(body: Property[]) { - this.type = Syntax.ClassBody; - this.body = body; - } -} - -export class ClassDeclaration { - readonly type: string; - readonly id: Identifier | null; - readonly superClass: Identifier | null; - readonly body: ClassBody; - constructor(id: Identifier | null, superClass: Identifier | null, body: ClassBody) { - this.type = Syntax.ClassDeclaration; - this.id = id; - this.superClass = superClass; - this.body = body; - } -} - -export class ClassExpression { - readonly type: string; - readonly id: Identifier | null; - readonly superClass: Identifier | null; - readonly body: ClassBody; - constructor(id: Identifier | null, superClass: Identifier | null, body: ClassBody) { - this.type = Syntax.ClassExpression; - this.id = id; - this.superClass = superClass; - this.body = body; - } -} - -export class ComputedMemberExpression { - readonly type: string; - readonly computed: boolean; - readonly object: Expression; - readonly property: Expression; - readonly optional: boolean; - constructor(object: Expression, property: Expression, optional: boolean) { - this.type = Syntax.MemberExpression; - this.computed = true; - this.object = object; - this.property = property; - this.optional = optional; - } -} - -export class ConditionalExpression { - readonly type: string; - readonly test: Expression; - readonly consequent: Expression; - readonly alternate: Expression; - constructor(test: Expression, consequent: Expression, alternate: Expression) { - this.type = Syntax.ConditionalExpression; - this.test = test; - this.consequent = consequent; - this.alternate = alternate; - } -} - -export class ContinueStatement { - readonly type: string; - readonly label: Identifier | null; - constructor(label: Identifier | null) { - this.type = Syntax.ContinueStatement; - this.label = label; - } -} - -export class DebuggerStatement { - readonly type: string; - constructor() { - this.type = Syntax.DebuggerStatement; - } -} - -export class Directive { - readonly type: string; - readonly expression: Expression; - readonly directive: string; - constructor(expression: Expression, directive: string) { - this.type = Syntax.ExpressionStatement; - this.expression = expression; - this.directive = directive; - } -} - -export class DoWhileStatement { - readonly type: string; - readonly body: Statement; - readonly test: Expression; - constructor(body: Statement, test: Expression) { - this.type = Syntax.DoWhileStatement; - this.body = body; - this.test = test; - } -} - -export class EmptyStatement { - readonly type: string; - constructor() { - this.type = Syntax.EmptyStatement; - } -} - -export class ExportAllDeclaration { - readonly type: string; - readonly source: Literal; - constructor(source: Literal) { - this.type = Syntax.ExportAllDeclaration; - this.source = source; - } -} - -export class ExportDefaultDeclaration { - readonly type: string; - readonly declaration: ExportableDefaultDeclaration; - constructor(declaration: ExportableDefaultDeclaration) { - this.type = Syntax.ExportDefaultDeclaration; - this.declaration = declaration; - } -} - -export class ExportNamedDeclaration { - readonly type: string; - readonly declaration: ExportableNamedDeclaration | null; - readonly specifiers: ExportSpecifier[]; - readonly source: Literal | null; - constructor(declaration: ExportableNamedDeclaration | null, specifiers: ExportSpecifier[], source: Literal | null) { - this.type = Syntax.ExportNamedDeclaration; - this.declaration = declaration; - this.specifiers = specifiers; - this.source = source; - } -} - -export class ExportSpecifier { - readonly type: string; - readonly exported: Identifier; - readonly local: Identifier; - constructor(local: Identifier, exported: Identifier) { - this.type = Syntax.ExportSpecifier; - this.exported = exported; - this.local = local; - } -} - -export class ExpressionStatement { - readonly type: string; - readonly expression: Expression; - constructor(expression: Expression) { - this.type = Syntax.ExpressionStatement; - this.expression = expression; - } -} - -export class ForInStatement { - readonly type: string; - readonly left: Expression; - readonly right: Expression; - readonly body: Statement; - readonly each: boolean; - constructor(left: Expression, right: Expression, body: Statement) { - this.type = Syntax.ForInStatement; - this.left = left; - this.right = right; - this.body = body; - this.each = false; - } -} - -export class ForOfStatement { - readonly type: string; - readonly await: boolean; - readonly left: Expression; - readonly right: Expression; - readonly body: Statement; - constructor(left: Expression, right: Expression, body: Statement, _await: boolean) { - this.type = Syntax.ForOfStatement; - this.await = _await; - this.left = left; - this.right = right; - this.body = body; - } -} - -export class ForStatement { - readonly type: string; - readonly init: Expression | null; - readonly test: Expression | null; - readonly update: Expression | null; - body: Statement; - constructor(init: Expression | null, test: Expression | null, update: Expression | null, body: Statement) { - this.type = Syntax.ForStatement; - this.init = init; - this.test = test; - this.update = update; - this.body = body; - } -} - -export class FunctionDeclaration { - readonly type: string; - readonly id: Identifier | null; - readonly params: FunctionParameter[]; - readonly body: BlockStatement; - readonly generator: boolean; - readonly expression: boolean; - readonly async: boolean; - constructor(id: Identifier | null, params: FunctionParameter[], body: BlockStatement, generator: boolean) { - this.type = Syntax.FunctionDeclaration; - this.id = id; - this.params = params; - this.body = body; - this.generator = generator; - this.expression = false; - this.async = false; - } -} - -export class FunctionExpression { - readonly type: string; - readonly id: Identifier | null; - readonly params: FunctionParameter[]; - readonly body: BlockStatement; - readonly generator: boolean; - readonly expression: boolean; - readonly async: boolean; - constructor(id: Identifier | null, params: FunctionParameter[], body: BlockStatement, generator: boolean) { - this.type = Syntax.FunctionExpression; - this.id = id; - this.params = params; - this.body = body; - this.generator = generator; - this.expression = false; - this.async = false; - } -} - -export class Identifier { - readonly type: string; - readonly name: string; - constructor(name) { - this.type = Syntax.Identifier; - this.name = name; - } -} - -export class IfStatement { - readonly type: string; - readonly test: Expression; - readonly consequent: Statement; - readonly alternate: Statement | null; - constructor(test: Expression, consequent: Statement, alternate: Statement | null) { - this.type = Syntax.IfStatement; - this.test = test; - this.consequent = consequent; - this.alternate = alternate; - } -} - -export class Import { - readonly type: string; - constructor() { - this.type = Syntax.Import; - } -} - -export class ImportDeclaration { - readonly type: string; - readonly specifiers: ImportDeclarationSpecifier[]; - readonly source: Literal; - constructor(specifiers, source) { - this.type = Syntax.ImportDeclaration; - this.specifiers = specifiers; - this.source = source; - } -} - -export class ImportDefaultSpecifier { - readonly type: string; - readonly local: Identifier; - constructor(local: Identifier) { - this.type = Syntax.ImportDefaultSpecifier; - this.local = local; - } -} - -export class ImportNamespaceSpecifier { - readonly type: string; - readonly local: Identifier; - constructor(local: Identifier) { - this.type = Syntax.ImportNamespaceSpecifier; - this.local = local; - } -} - -export class ImportSpecifier { - readonly type: string; - readonly local: Identifier; - readonly imported: Identifier; - constructor(local: Identifier, imported: Identifier) { - this.type = Syntax.ImportSpecifier; - this.local = local; - this.imported = imported; - } -} - -export class LabeledStatement { - readonly type: string; - readonly label: Identifier; - readonly body: Statement; - constructor(label: Identifier, body: Statement) { - this.type = Syntax.LabeledStatement; - this.label = label; - this.body = body; - } -} - -export class Literal { - readonly type: string; - readonly value: boolean | number | string | null; - readonly raw: string; - constructor(value: boolean | number | string | null, raw: string) { - this.type = Syntax.Literal; - this.value = value; - this.raw = raw; - } -} - -export class MetaProperty { - readonly type: string; - readonly meta: Identifier; - readonly property: Identifier; - constructor(meta: Identifier, property: Identifier) { - this.type = Syntax.MetaProperty; - this.meta = meta; - this.property = property; - } -} - -export class MethodDefinition { - readonly type: string; - readonly key: Expression | null; - readonly computed: boolean; - readonly value: AsyncFunctionExpression | FunctionExpression | null; - readonly kind: string; - readonly static: boolean; - constructor(key: Expression | null, computed: boolean, value: AsyncFunctionExpression | FunctionExpression | null, kind: string, isStatic: boolean) { - this.type = Syntax.MethodDefinition; - this.key = key; - this.computed = computed; - this.value = value; - this.kind = kind; - this.static = isStatic; - } -} - -export class Module { - readonly type: string; - readonly body: StatementListItem[]; - readonly sourceType: string; - constructor(body: StatementListItem[]) { - this.type = Syntax.Program; - this.body = body; - this.sourceType = 'module'; - } -} - -export class NewExpression { - readonly type: string; - readonly callee: Expression; - readonly arguments: ArgumentListElement[]; - constructor(callee: Expression, args: ArgumentListElement[]) { - this.type = Syntax.NewExpression; - this.callee = callee; - this.arguments = args; - } -} - -export class ObjectExpression { - readonly type: string; - readonly properties: ObjectExpressionProperty[]; - constructor(properties: ObjectExpressionProperty[]) { - this.type = Syntax.ObjectExpression; - this.properties = properties; - } -} - -export class ObjectPattern { - readonly type: string; - readonly properties: ObjectPatternProperty[]; - constructor(properties: ObjectPatternProperty[]) { - this.type = Syntax.ObjectPattern; - this.properties = properties; - } -} - -export class Property { - readonly type: string; - readonly key: PropertyKey; - readonly computed: boolean; - readonly value: PropertyValue | null; - readonly kind: string; - readonly method: boolean; - readonly shorthand: boolean; - constructor(kind: string, key: PropertyKey, computed: boolean, value: PropertyValue | null, method: boolean, shorthand: boolean) { - this.type = Syntax.Property; - this.key = key; - this.computed = computed; - this.value = value; - this.kind = kind; - this.method = method; - this.shorthand = shorthand; - } -} - -export class RegexLiteral { - readonly type: string; - readonly value: RegExp; - readonly raw: string; - readonly regex: { pattern: string; flags: string }; - constructor(value: RegExp, raw: string, pattern: string, flags: string) { - this.type = Syntax.Literal; - this.value = value; - this.raw = raw; - this.regex = { pattern, flags }; - } -} - -export class RestElement { - readonly type: string; - readonly argument: BindingIdentifier | BindingPattern; - constructor(argument: BindingIdentifier | BindingPattern) { - this.type = Syntax.RestElement; - this.argument = argument; - } -} - -export class ReturnStatement { - readonly type: string; - readonly argument: Expression | null; - constructor(argument: Expression | null) { - this.type = Syntax.ReturnStatement; - this.argument = argument; - } -} - -export class Script { - readonly type: string; - readonly body: StatementListItem[]; - readonly sourceType: string; - constructor(body: StatementListItem[]) { - this.type = Syntax.Program; - this.body = body; - this.sourceType = 'script'; - } -} - -export class SequenceExpression { - readonly type: string; - readonly expressions: Expression[]; - constructor(expressions: Expression[]) { - this.type = Syntax.SequenceExpression; - this.expressions = expressions; - } -} - -export class SpreadElement { - readonly type: string; - readonly argument: Expression; - constructor(argument: Expression) { - this.type = Syntax.SpreadElement; - this.argument = argument; - } -} - -export class StaticMemberExpression { - readonly type: string; - readonly computed: boolean; - readonly object: Expression; - readonly property: Expression; - readonly optional: boolean; - constructor(object: Expression, property: Expression, optional: boolean) { - this.type = Syntax.MemberExpression; - this.computed = false; - this.object = object; - this.property = property; - this.optional = optional; - } -} - -export class Super { - readonly type: string; - constructor() { - this.type = Syntax.Super; - } -} - -export class SwitchCase { - readonly type: string; - readonly test: Expression | null; - readonly consequent: Statement[]; - constructor(test: Expression, consequent: Statement[]) { - this.type = Syntax.SwitchCase; - this.test = test; - this.consequent = consequent; - } -} - -export class SwitchStatement { - readonly type: string; - readonly discriminant: Expression; - readonly cases: SwitchCase[]; - constructor(discriminant: Expression, cases: SwitchCase[]) { - this.type = Syntax.SwitchStatement; - this.discriminant = discriminant; - this.cases = cases; - } -} - -export class TaggedTemplateExpression { - readonly type: string; - readonly tag: Expression; - readonly quasi: TemplateLiteral; - constructor(tag: Expression, quasi: TemplateLiteral) { - this.type = Syntax.TaggedTemplateExpression; - this.tag = tag; - this.quasi = quasi; - } -} - -interface TemplateElementValue { - cooked: string | null; - raw: string; -} - -export class TemplateElement { - readonly type: string; - readonly value: TemplateElementValue; - readonly tail: boolean; - constructor(value: TemplateElementValue, tail: boolean) { - this.type = Syntax.TemplateElement; - this.value = value; - this.tail = tail; - } -} - -export class TemplateLiteral { - readonly type: string; - readonly quasis: TemplateElement[]; - readonly expressions: Expression[]; - constructor(quasis: TemplateElement[], expressions: Expression[]) { - this.type = Syntax.TemplateLiteral; - this.quasis = quasis; - this.expressions = expressions; - } -} - -export class ThisExpression { - readonly type: string; - constructor() { - this.type = Syntax.ThisExpression; - } -} - -export class ThrowStatement { - readonly type: string; - readonly argument: Expression; - constructor(argument: Expression) { - this.type = Syntax.ThrowStatement; - this.argument = argument; - } -} - -export class TryStatement { - readonly type: string; - readonly block: BlockStatement; - readonly handler: CatchClause | null; - readonly finalizer: BlockStatement | null; - constructor(block: BlockStatement, handler: CatchClause | null, finalizer: BlockStatement | null) { - this.type = Syntax.TryStatement; - this.block = block; - this.handler = handler; - this.finalizer = finalizer; - } -} - -export class UnaryExpression { - readonly type: string; - readonly operator: string; - readonly argument: Expression; - readonly prefix: boolean; - constructor(operator, argument) { - this.type = Syntax.UnaryExpression; - this.operator = operator; - this.argument = argument; - this.prefix = true; - } -} - -export class UpdateExpression { - readonly type: string; - readonly operator: string; - readonly argument: Expression; - readonly prefix: boolean; - constructor(operator, argument, prefix) { - this.type = Syntax.UpdateExpression; - this.operator = operator; - this.argument = argument; - this.prefix = prefix; - } -} - -export class VariableDeclaration { - readonly type: string; - readonly declarations: VariableDeclarator[]; - readonly kind: string; - constructor(declarations: VariableDeclarator[], kind: string) { - this.type = Syntax.VariableDeclaration; - this.declarations = declarations; - this.kind = kind; - } -} - -export class VariableDeclarator { - readonly type: string; - readonly id: BindingIdentifier | BindingPattern; - readonly init: Expression | null; - constructor(id: BindingIdentifier | BindingPattern, init: Expression | null) { - this.type = Syntax.VariableDeclarator; - this.id = id; - this.init = init; - } -} - -export class WhileStatement { - readonly type: string; - readonly test: Expression; - readonly body: Statement; - constructor(test: Expression, body: Statement) { - this.type = Syntax.WhileStatement; - this.test = test; - this.body = body; - } -} - -export class WithStatement { - readonly type: string; - readonly object: Expression; - readonly body: Statement; - constructor(object: Expression, body: Statement) { - this.type = Syntax.WithStatement; - this.object = object; - this.body = body; - } -} - -export class YieldExpression { - readonly type: string; - readonly argument: Expression | null; - readonly delegate: boolean; - constructor(argument: Expression | null, delegate: boolean) { - this.type = Syntax.YieldExpression; - this.argument = argument; - this.delegate = delegate; - } -} +import { Syntax } from './syntax'; + +export type ArgumentListElement = Expression | SpreadElement; +export type ArrayExpressionElement = Expression | SpreadElement | null; +export type ArrayPatternElement = AssignmentPattern | BindingIdentifier | BindingPattern | RestElement | null; +export type BindingPattern = ArrayPattern | ObjectPattern; +export type BindingIdentifier = Identifier; +export type ChainElement = CallExpression | ComputedMemberExpression | StaticMemberExpression; +export type Declaration = AsyncFunctionDeclaration | ClassDeclaration | ExportDeclaration | FunctionDeclaration | ImportDeclaration | VariableDeclaration; +export type ExportableDefaultDeclaration = BindingIdentifier | BindingPattern | ClassDeclaration | Expression | FunctionDeclaration; +export type ExportableNamedDeclaration = AsyncFunctionDeclaration | ClassDeclaration | FunctionDeclaration | VariableDeclaration; +export type ExportDeclaration = ExportAllDeclaration | ExportDefaultDeclaration | ExportNamedDeclaration; +export type Expression = ArrayExpression | ArrowFunctionExpression | AssignmentExpression | + AwaitExpression | BinaryExpression | CallExpression | ChainExpression | ClassExpression | ComputedMemberExpression | + ConditionalExpression | Identifier | FunctionExpression | Literal | NewExpression | ObjectExpression | + RegexLiteral | SequenceExpression | StaticMemberExpression | TaggedTemplateExpression | + ThisExpression | UnaryExpression | UpdateExpression | YieldExpression; +export type FunctionParameter = AssignmentPattern | BindingIdentifier | BindingPattern; +export type ImportDeclarationSpecifier = ImportDefaultSpecifier | ImportNamespaceSpecifier | ImportSpecifier; +export type ObjectExpressionProperty = PropertyDefinition | SpreadElement; +export type ObjectPatternProperty = PropertyDefinition | RestElement; +export type Statement = AsyncFunctionDeclaration | BreakStatement | ContinueStatement | DebuggerStatement | DoWhileStatement | + EmptyStatement | ExpressionStatement | Directive | ForStatement | ForInStatement | ForOfStatement | + FunctionDeclaration | IfStatement | ReturnStatement | SwitchStatement | ThrowStatement | + TryStatement | VariableDeclaration | WhileStatement | WithStatement; +export type PropertyKey = Identifier | Literal | PrivateIdentifier; +export type PropertyValue = AssignmentPattern | BindingIdentifier | BindingPattern | FunctionExpression; +export type StatementListItem = Declaration | Statement; + +export class ArrayExpression { + readonly type: string; + readonly elements: ArrayExpressionElement[]; + constructor(elements: ArrayExpressionElement[]) { + this.type = Syntax.ArrayExpression; + this.elements = elements; + } +} + +export class ArrayPattern { + readonly type: string; + readonly elements: ArrayPatternElement[]; + constructor(elements: ArrayPatternElement[]) { + this.type = Syntax.ArrayPattern; + this.elements = elements; + } +} + +export class ArrowFunctionExpression { + readonly type: string; + readonly id: Identifier | null; + readonly params: FunctionParameter[]; + readonly body: BlockStatement | Expression; + readonly generator: boolean; + readonly expression: boolean; + readonly async: boolean; + constructor(params: FunctionParameter[], body: BlockStatement | Expression, expression: boolean, isAsync: boolean) { + this.type = Syntax.ArrowFunctionExpression; + this.id = null; + this.params = params; + this.body = body; + this.generator = false; + this.expression = expression; + this.async = isAsync; + } +} + +export class AssignmentExpression { + readonly type: string; + readonly operator: string; + readonly left: Expression; + readonly right: Expression; + constructor(operator: string, left: Expression, right: Expression) { + this.type = Syntax.AssignmentExpression; + this.operator = operator; + this.left = left; + this.right = right; + } +} + +export class AssignmentPattern { + readonly type: string; + readonly left: BindingIdentifier | BindingPattern; + readonly right: Expression; + constructor(left: BindingIdentifier | BindingPattern, right: Expression) { + this.type = Syntax.AssignmentPattern; + this.left = left; + this.right = right; + } +} + +export class AsyncFunctionDeclaration { + readonly type: string; + readonly id: Identifier | null; + readonly params: FunctionParameter[]; + readonly body: BlockStatement; + readonly generator: boolean; + readonly expression: boolean; + readonly async: boolean; + constructor(id: Identifier | null, params: FunctionParameter[], body: BlockStatement, generator: boolean) { + this.type = Syntax.FunctionDeclaration; + this.id = id; + this.params = params; + this.body = body; + this.generator = generator; + this.expression = false; + this.async = true; + } +} + +export class AwaitExpression { + readonly type: string; + readonly argument: Expression; + constructor(argument: Expression) { + this.type = Syntax.AwaitExpression; + this.argument = argument; + } +} + +export class BinaryExpression { + readonly type: string; + readonly operator: string; + readonly left: Expression; + readonly right: Expression; + constructor(operator: string, left: Expression, right: Expression) { + const logical = (operator === '||' || operator === '&&' || operator === '??'); + this.type = logical ? Syntax.LogicalExpression : Syntax.BinaryExpression; + this.operator = operator; + this.left = left; + this.right = right; + } +} + +export class BlockStatement { + readonly type: string; + readonly body: Statement[]; + constructor(body) { + this.type = Syntax.BlockStatement; + this.body = body; + } +} + +export class BreakStatement { + readonly type: string; + readonly label: Identifier | null; + constructor(label: Identifier | null) { + this.type = Syntax.BreakStatement; + this.label = label; + } +} + +export class CallExpression { + readonly type: string; + readonly callee: Expression | Import; + readonly arguments: ArgumentListElement[]; + readonly optional: boolean; + constructor(callee: Expression | Import, args: ArgumentListElement[], optional: boolean) { + this.type = Syntax.CallExpression; + this.callee = callee; + this.arguments = args; + this.optional = optional; + } +} + +export class CatchClause { + readonly type: string; + readonly param: BindingIdentifier | BindingPattern | null; + readonly body: BlockStatement; + constructor(param: BindingIdentifier | BindingPattern | null, body: BlockStatement) { + this.type = Syntax.CatchClause; + this.param = param; + this.body = body; + } +} + +export class ChainExpression { + readonly type: string; + readonly expression: ChainElement; + constructor(expression: ChainElement) { + this.type = Syntax.ChainExpression; + this.expression = expression; + } +} + +export class ClassBody { + readonly type: string; + readonly body: (MethodDefinition | PropertyDefinition)[]; + constructor(body: PropertyDefinition[]) { + this.type = Syntax.ClassBody; + this.body = body; + } +} + +export class ClassDeclaration { + readonly type: string; + readonly id: Identifier | null; + readonly superClass: Identifier | null; + readonly body: ClassBody; + constructor(id: Identifier | null, superClass: Identifier | null, body: ClassBody) { + this.type = Syntax.ClassDeclaration; + this.id = id; + this.superClass = superClass; + this.body = body; + } +} + +export class ClassExpression { + readonly type: string; + readonly id: Identifier | null; + readonly superClass: Identifier | null; + readonly body: ClassBody; + constructor(id: Identifier | null, superClass: Identifier | null, body: ClassBody) { + this.type = Syntax.ClassExpression; + this.id = id; + this.superClass = superClass; + this.body = body; + } +} + +export class ComputedMemberExpression { + readonly type: string; + readonly computed: boolean; + readonly object: Expression; + readonly property: Expression | PrivateIdentifier; + readonly optional: boolean; + constructor(object: Expression, property: Expression, optional: boolean) { + this.type = Syntax.MemberExpression; + this.computed = true; + this.object = object; + this.property = property; + this.optional = optional; + } +} + +export class ConditionalExpression { + readonly type: string; + readonly test: Expression; + readonly consequent: Expression; + readonly alternate: Expression; + constructor(test: Expression, consequent: Expression, alternate: Expression) { + this.type = Syntax.ConditionalExpression; + this.test = test; + this.consequent = consequent; + this.alternate = alternate; + } +} + +export class ContinueStatement { + readonly type: string; + readonly label: Identifier | null; + constructor(label: Identifier | null) { + this.type = Syntax.ContinueStatement; + this.label = label; + } +} + +export class DebuggerStatement { + readonly type: string; + constructor() { + this.type = Syntax.DebuggerStatement; + } +} + +export class Directive { + readonly type: string; + readonly expression: Expression; + readonly directive: string; + constructor(expression: Expression, directive: string) { + this.type = Syntax.ExpressionStatement; + this.expression = expression; + this.directive = directive; + } +} + +export class DoWhileStatement { + readonly type: string; + readonly body: Statement; + readonly test: Expression; + constructor(body: Statement, test: Expression) { + this.type = Syntax.DoWhileStatement; + this.body = body; + this.test = test; + } +} + +export class EmptyStatement { + readonly type: string; + constructor() { + this.type = Syntax.EmptyStatement; + } +} + +export class ExportAllDeclaration { + readonly type: string; + readonly source: Literal; + constructor(source: Literal) { + this.type = Syntax.ExportAllDeclaration; + this.source = source; + } +} + +export class ExportDefaultDeclaration { + readonly type: string; + readonly declaration: ExportableDefaultDeclaration; + constructor(declaration: ExportableDefaultDeclaration) { + this.type = Syntax.ExportDefaultDeclaration; + this.declaration = declaration; + } +} + +export class ExportNamedDeclaration { + readonly type: string; + readonly declaration: ExportableNamedDeclaration | null; + readonly specifiers: ExportSpecifier[]; + readonly source: Literal | null; + constructor(declaration: ExportableNamedDeclaration | null, specifiers: ExportSpecifier[], source: Literal | null) { + this.type = Syntax.ExportNamedDeclaration; + this.declaration = declaration; + this.specifiers = specifiers; + this.source = source; + } +} + +export class ExportSpecifier { + readonly type: string; + readonly exported: Identifier; + readonly local: Identifier; + constructor(local: Identifier, exported: Identifier) { + this.type = Syntax.ExportSpecifier; + this.exported = exported; + this.local = local; + } +} + +export class ExpressionStatement { + readonly type: string; + readonly expression: Expression; + constructor(expression: Expression) { + this.type = Syntax.ExpressionStatement; + this.expression = expression; + } +} + +export class ForInStatement { + readonly type: string; + readonly left: Expression; + readonly right: Expression; + readonly body: Statement; + readonly each: boolean; + constructor(left: Expression, right: Expression, body: Statement) { + this.type = Syntax.ForInStatement; + this.left = left; + this.right = right; + this.body = body; + this.each = false; + } +} + +export class ForOfStatement { + readonly type: string; + readonly await: boolean; + readonly left: Expression; + readonly right: Expression; + readonly body: Statement; + constructor(left: Expression, right: Expression, body: Statement, _await: boolean) { + this.type = Syntax.ForOfStatement; + this.await = _await; + this.left = left; + this.right = right; + this.body = body; + } +} + +export class ForStatement { + readonly type: string; + readonly init: Expression | null; + readonly test: Expression | null; + readonly update: Expression | null; + body: Statement; + constructor(init: Expression | null, test: Expression | null, update: Expression | null, body: Statement) { + this.type = Syntax.ForStatement; + this.init = init; + this.test = test; + this.update = update; + this.body = body; + } +} + +export class FunctionDeclaration { + readonly type: string; + readonly id: Identifier | null; + readonly params: FunctionParameter[]; + readonly body: BlockStatement; + readonly generator: boolean; + readonly expression: boolean; + readonly async: boolean; + constructor(id: Identifier | null, params: FunctionParameter[], body: BlockStatement, generator: boolean) { + this.type = Syntax.FunctionDeclaration; + this.id = id; + this.params = params; + this.body = body; + this.generator = generator; + this.expression = false; + this.async = false; + } +} + +export class FunctionExpression { + readonly type: string; + readonly id: Identifier | null; + readonly params: FunctionParameter[]; + readonly body: BlockStatement; + readonly generator: boolean; + readonly expression: boolean; + readonly async: boolean; + constructor(id: Identifier | null, params: FunctionParameter[], body: BlockStatement, generator: boolean, isAsync: boolean) { + this.type = Syntax.FunctionExpression; + this.id = id; + this.params = params; + this.body = body; + this.generator = generator; + this.expression = false; + this.async = isAsync; + } +} + +export class Identifier { + readonly type: string; + readonly name: string; + constructor(name) { + this.type = Syntax.Identifier; + this.name = name; + } +} + +export class IfStatement { + readonly type: string; + readonly test: Expression; + readonly consequent: Statement; + readonly alternate: Statement | null; + constructor(test: Expression, consequent: Statement, alternate: Statement | null) { + this.type = Syntax.IfStatement; + this.test = test; + this.consequent = consequent; + this.alternate = alternate; + } +} + +export class Import { + readonly type: string; + constructor() { + this.type = Syntax.Import; + } +} + +export class ImportDeclaration { + readonly type: string; + readonly specifiers: ImportDeclarationSpecifier[]; + readonly source: Literal; + constructor(specifiers, source) { + this.type = Syntax.ImportDeclaration; + this.specifiers = specifiers; + this.source = source; + } +} + +export class ImportDefaultSpecifier { + readonly type: string; + readonly local: Identifier; + constructor(local: Identifier) { + this.type = Syntax.ImportDefaultSpecifier; + this.local = local; + } +} + +export class ImportNamespaceSpecifier { + readonly type: string; + readonly local: Identifier; + constructor(local: Identifier) { + this.type = Syntax.ImportNamespaceSpecifier; + this.local = local; + } +} + +export class ImportSpecifier { + readonly type: string; + readonly local: Identifier; + readonly imported: Identifier; + constructor(local: Identifier, imported: Identifier) { + this.type = Syntax.ImportSpecifier; + this.local = local; + this.imported = imported; + } +} + +export class LabeledStatement { + readonly type: string; + readonly label: Identifier; + readonly body: Statement; + constructor(label: Identifier, body: Statement) { + this.type = Syntax.LabeledStatement; + this.label = label; + this.body = body; + } +} + +export class Literal { + readonly type: string; + readonly value: boolean | number | string | null; + readonly raw: string; + constructor(value: boolean | number | string | null, raw: string) { + this.type = Syntax.Literal; + this.value = value; + this.raw = raw; + } +} + +export class MetaProperty { + readonly type: string; + readonly meta: Identifier; + readonly property: Identifier; + constructor(meta: Identifier, property: Identifier) { + this.type = Syntax.MetaProperty; + this.meta = meta; + this.property = property; + } +} + +export class MethodDefinition { + readonly type: string; + readonly key: Expression | PrivateIdentifier | null; + readonly computed: boolean; + readonly value: FunctionExpression | null; + readonly kind: string; + readonly static: boolean; + constructor(key: Expression | null, computed: boolean, value: FunctionExpression | null, kind: string, isStatic: boolean) { + this.type = Syntax.MethodDefinition; + this.key = key; + this.computed = computed; + this.value = value; + this.kind = kind; + this.static = isStatic; + } +} + +export class Module { + readonly type: string; + readonly body: StatementListItem[]; + readonly sourceType: string; + constructor(body: StatementListItem[]) { + this.type = Syntax.Program; + this.body = body; + this.sourceType = 'module'; + } +} + +export class NewExpression { + readonly type: string; + readonly callee: Expression; + readonly arguments: ArgumentListElement[]; + constructor(callee: Expression, args: ArgumentListElement[]) { + this.type = Syntax.NewExpression; + this.callee = callee; + this.arguments = args; + } +} + +export class ObjectExpression { + readonly type: string; + readonly properties: ObjectExpressionProperty[]; + constructor(properties: ObjectExpressionProperty[]) { + this.type = Syntax.ObjectExpression; + this.properties = properties; + } +} + +export class ObjectPattern { + readonly type: string; + readonly properties: ObjectPatternProperty[]; + constructor(properties: ObjectPatternProperty[]) { + this.type = Syntax.ObjectPattern; + this.properties = properties; + } +} + +export class PrivateIdentifier { + readonly type: string; + readonly name: string; + constructor(name) { + this.type = Syntax.PrivateIdentifier; + this.name = name; + } +} + +export class Property { + readonly type: string; + readonly key: PropertyKey; + readonly computed: boolean; + readonly value: PropertyValue | null; + readonly kind: string; + readonly method: boolean; + readonly shorthand: boolean; + constructor(kind: string, key: PropertyKey, computed: boolean, value: PropertyValue | null, method: boolean, shorthand: boolean) { + this.type = Syntax.Property; + this.key = key; + this.computed = computed; + this.value = value; + this.kind = kind; + this.method = method; + this.shorthand = shorthand; + } +} + +export class PropertyDefinition { + readonly type: string; + readonly key: PropertyKey; + readonly computed: boolean; + readonly value: PropertyValue | null; + readonly static: boolean; + constructor(key: PropertyKey, computed: boolean, value: PropertyValue | null, isStatic: boolean) { + this.type = Syntax.Property; + this.key = key; + this.computed = computed; + this.value = value; + this.static = isStatic; + } +} + +export class RegexLiteral { + readonly type: string; + readonly value: RegExp; + readonly raw: string; + readonly regex: { pattern: string; flags: string }; + constructor(value: RegExp, raw: string, pattern: string, flags: string) { + this.type = Syntax.Literal; + this.value = value; + this.raw = raw; + this.regex = { pattern, flags }; + } +} + +export class RestElement { + readonly type: string; + readonly argument: BindingIdentifier | BindingPattern; + constructor(argument: BindingIdentifier | BindingPattern) { + this.type = Syntax.RestElement; + this.argument = argument; + } +} + +export class ReturnStatement { + readonly type: string; + readonly argument: Expression | null; + constructor(argument: Expression | null) { + this.type = Syntax.ReturnStatement; + this.argument = argument; + } +} + +export class Script { + readonly type: string; + readonly body: StatementListItem[]; + readonly sourceType: string; + constructor(body: StatementListItem[]) { + this.type = Syntax.Program; + this.body = body; + this.sourceType = 'script'; + } +} + +export class SequenceExpression { + readonly type: string; + readonly expressions: Expression[]; + constructor(expressions: Expression[]) { + this.type = Syntax.SequenceExpression; + this.expressions = expressions; + } +} + +export class SpreadElement { + readonly type: string; + readonly argument: Expression; + constructor(argument: Expression) { + this.type = Syntax.SpreadElement; + this.argument = argument; + } +} + +export class StaticMemberExpression { + readonly type: string; + readonly computed: boolean; + readonly object: Expression; + readonly property: Expression | PrivateIdentifier; + readonly optional: boolean; + constructor(object: Expression, property: Expression, optional: boolean) { + this.type = Syntax.MemberExpression; + this.computed = false; + this.object = object; + this.property = property; + this.optional = optional; + } +} + +export class Super { + readonly type: string; + constructor() { + this.type = Syntax.Super; + } +} + +export class SwitchCase { + readonly type: string; + readonly test: Expression | null; + readonly consequent: Statement[]; + constructor(test: Expression, consequent: Statement[]) { + this.type = Syntax.SwitchCase; + this.test = test; + this.consequent = consequent; + } +} + +export class SwitchStatement { + readonly type: string; + readonly discriminant: Expression; + readonly cases: SwitchCase[]; + constructor(discriminant: Expression, cases: SwitchCase[]) { + this.type = Syntax.SwitchStatement; + this.discriminant = discriminant; + this.cases = cases; + } +} + +export class TaggedTemplateExpression { + readonly type: string; + readonly tag: Expression; + readonly quasi: TemplateLiteral; + constructor(tag: Expression, quasi: TemplateLiteral) { + this.type = Syntax.TaggedTemplateExpression; + this.tag = tag; + this.quasi = quasi; + } +} + +interface TemplateElementValue { + cooked: string | null; + raw: string; +} + +export class TemplateElement { + readonly type: string; + readonly value: TemplateElementValue; + readonly tail: boolean; + constructor(value: TemplateElementValue, tail: boolean) { + this.type = Syntax.TemplateElement; + this.value = value; + this.tail = tail; + } +} + +export class TemplateLiteral { + readonly type: string; + readonly quasis: TemplateElement[]; + readonly expressions: Expression[]; + constructor(quasis: TemplateElement[], expressions: Expression[]) { + this.type = Syntax.TemplateLiteral; + this.quasis = quasis; + this.expressions = expressions; + } +} + +export class ThisExpression { + readonly type: string; + constructor() { + this.type = Syntax.ThisExpression; + } +} + +export class ThrowStatement { + readonly type: string; + readonly argument: Expression; + constructor(argument: Expression) { + this.type = Syntax.ThrowStatement; + this.argument = argument; + } +} + +export class TryStatement { + readonly type: string; + readonly block: BlockStatement; + readonly handler: CatchClause | null; + readonly finalizer: BlockStatement | null; + constructor(block: BlockStatement, handler: CatchClause | null, finalizer: BlockStatement | null) { + this.type = Syntax.TryStatement; + this.block = block; + this.handler = handler; + this.finalizer = finalizer; + } +} + +export class UnaryExpression { + readonly type: string; + readonly operator: string; + readonly argument: Expression; + readonly prefix: boolean; + constructor(operator, argument) { + this.type = Syntax.UnaryExpression; + this.operator = operator; + this.argument = argument; + this.prefix = true; + } +} + +export class UpdateExpression { + readonly type: string; + readonly operator: string; + readonly argument: Expression; + readonly prefix: boolean; + constructor(operator, argument, prefix) { + this.type = Syntax.UpdateExpression; + this.operator = operator; + this.argument = argument; + this.prefix = prefix; + } +} + +export class VariableDeclaration { + readonly type: string; + readonly declarations: VariableDeclarator[]; + readonly kind: string; + constructor(declarations: VariableDeclarator[], kind: string) { + this.type = Syntax.VariableDeclaration; + this.declarations = declarations; + this.kind = kind; + } +} + +export class VariableDeclarator { + readonly type: string; + readonly id: BindingIdentifier | BindingPattern; + readonly init: Expression | null; + constructor(id: BindingIdentifier | BindingPattern, init: Expression | null) { + this.type = Syntax.VariableDeclarator; + this.id = id; + this.init = init; + } +} + +export class WhileStatement { + readonly type: string; + readonly test: Expression; + readonly body: Statement; + constructor(test: Expression, body: Statement) { + this.type = Syntax.WhileStatement; + this.test = test; + this.body = body; + } +} + +export class WithStatement { + readonly type: string; + readonly object: Expression; + readonly body: Statement; + constructor(object: Expression, body: Statement) { + this.type = Syntax.WithStatement; + this.object = object; + this.body = body; + } +} + +export class YieldExpression { + readonly type: string; + readonly argument: Expression | null; + readonly delegate: boolean; + constructor(argument: Expression | null, delegate: boolean) { + this.type = Syntax.YieldExpression; + this.argument = argument; + this.delegate = delegate; + } +} diff --git a/src/parser.ts b/src/parser.ts index 39d4f9b21..f1e783db0 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -6,13 +6,16 @@ import { Comment, RawToken, Scanner, SourceLocation } from './scanner'; import { Syntax } from './syntax'; import { Token, TokenName } from './token'; -interface Config { - range: boolean; - loc: boolean; - source: string | null; - tokens: boolean; - comment: boolean; - tolerant: boolean; +export interface Config { + range?: boolean; + loc?: boolean; + source?: string | null; + tokens?: boolean; + comment?: boolean; + tolerant?: boolean; + jsx?: boolean; + sourceType?: 'script' | 'module'; + attachComment?: boolean; } interface Context { @@ -82,7 +85,7 @@ export class Parser { startMarker: Marker; lastMarker: Marker; - constructor(code: string, options: any = {}, delegate) { + constructor(code: string, options: Config = {}, delegate) { this.config = { range: (typeof options.range === 'boolean') && options.range, loc: (typeof options.loc === 'boolean') && options.loc, @@ -98,9 +101,9 @@ export class Parser { this.delegate = delegate; this.errorHandler = new ErrorHandler(); - this.errorHandler.tolerant = this.config.tolerant; + this.errorHandler.tolerant = this.config.tolerant == true; this.scanner = new Scanner(code, this.errorHandler); - this.scanner.trackComment = this.config.comment; + this.scanner.trackComment = this.config.comment == true; this.operatorPrecedence = { ')': 0, @@ -700,6 +703,8 @@ export class Parser { expr = this.finalize(node, new Node.ThisExpression()); } else if (this.matchKeyword('class')) { expr = this.parseClassExpression(); + } else if (this.matchKeyword('new')) { + expr = this.parseNewExpression(); } else if (this.matchImportCall()) { expr = this.parseImportCall(); } else if (this.matchImportMeta()) { @@ -789,7 +794,7 @@ export class Parser { const method = this.parsePropertyMethod(params); this.context.allowYield = previousAllowYield; - return this.finalize(node, new Node.FunctionExpression(null, params.params, method, isGenerator)); + return this.finalize(node, new Node.FunctionExpression(null, params.params, method, isGenerator, false)); } parsePropertyMethodAsyncFunction(isGenerator: boolean): Node.FunctionExpression { @@ -805,10 +810,10 @@ export class Parser { this.context.allowYield = previousAllowYield; this.context.isAsync = previousIsAsync; - return this.finalize(node, new Node.AsyncFunctionExpression(null, params.params, method, isGenerator)); + return this.finalize(node, new Node.FunctionExpression(null, params.params, method, isGenerator, true)); } - parseObjectPropertyKey(): Node.PropertyKey { + parseObjectPropertyKey(isPrivate: boolean = false): Node.PropertyKey { const node = this.createNode(); const token = this.nextToken(); @@ -827,7 +832,7 @@ export class Parser { case Token.BooleanLiteral: case Token.NullLiteral: case Token.Keyword: - key = this.finalize(node, new Node.Identifier(token.value)); + key = this.finalize(node, isPrivate ? new Node.PrivateIdentifier(token.value) : new Node.Identifier(token.value)); break; case Token.Punctuator: @@ -851,7 +856,7 @@ export class Parser { (key.type === Syntax.Literal && key.value === value); } - parseObjectProperty(hasProto): Node.Property { + parseObjectProperty(hasProto): Node.PropertyDefinition { const node = this.createNode(); const token = this.lookahead; @@ -901,7 +906,7 @@ export class Parser { kind = 'init'; computed = this.match('['); key = this.parseObjectPropertyKey(); - value = this.parseGeneratorMethod(); + value = this.parseGeneratorMethod(false); method = true; } else { @@ -1219,9 +1224,15 @@ export class Parser { token.type === Token.NullLiteral; } - parseIdentifierName(): Node.Identifier { - const node = this.createNode(); - const token = this.nextToken(); + parseIdentifierName(isPrivateField: boolean = false): Node.Identifier { + let node = this.createNode(); + let token = this.nextToken(); + if (token.value === '#') { + token = this.nextToken(); + if (isPrivateField) { + token.value = '#' + token.value; + } + } if (!this.isIdentifierName(token)) { this.throwUnexpectedToken(token); } @@ -1414,7 +1425,7 @@ export class Parser { if (!optional) { this.expect('.'); } - const property = this.parseIdentifierName(); + const property = this.parseIdentifierName(true); expr = this.finalize(this.startNode(startToken), new Node.StaticMemberExpression(expr, property, optional)); } else { @@ -1872,8 +1883,7 @@ export class Parser { if (this.context.strict && list.stricted) { this.tolerateUnexpectedToken(list.stricted, list.message); } - expr = isAsync ? this.finalize(node, new Node.AsyncArrowFunctionExpression(list.params, body, expression)) : - this.finalize(node, new Node.ArrowFunctionExpression(list.params, body, expression)); + expr = this.finalize(node, new Node.ArrowFunctionExpression(list.params, body, expression, isAsync)); this.context.strict = previousStrict; this.context.allowStrictDirective = previousAllowStrictDirective; @@ -2070,6 +2080,35 @@ export class Parser { return this.finalize(node, new Node.VariableDeclaration(declarations, kind)); } + /** + * This function checks to see if a property is initialized in a Class + * e.g. + * publicProp = 123; + * @returns {Boolean} + */ + isInitializedProperty(): any { + let state = this.scanner.saveState(); + this.scanner.scanComments(); + let next = this.scanner.lex(); + this.scanner.restoreState(state); + return this.lookahead.type === 3 && next.value === '='; + } + + /** + * This function checks to see if a property is declared in a Class + * e.g. + * publicProp; + * @returns {Boolean} + */ + isDeclaredProperty(): any { + let state = this.scanner.saveState(); + this.scanner.scanComments(); + let next = this.scanner.lex(); + this.scanner.restoreState(state); + return this.lookahead.type === 3 && next.value === ';' + || this.lookahead.type === 3 && next.lineNumber !== this.startMarker.line; + } + // https://tc39.github.io/ecma262/#sec-destructuring-binding-patterns parseBindingRestElement(params, kind?: string): Node.RestElement { @@ -2108,7 +2147,7 @@ export class Parser { return this.finalize(node, new Node.ArrayPattern(elements)); } - parsePropertyPattern(params, kind?: string): Node.Property { + parsePropertyPattern(params, kind?: string): Node.PropertyDefinition { const node = this.createNode(); let computed = false; @@ -3146,7 +3185,7 @@ export class Parser { : this.finalize(node, new Node.FunctionDeclaration(id, params, body, isGenerator)); } - parseFunctionExpression(): Node.AsyncFunctionExpression | Node.FunctionExpression { + parseFunctionExpression(): Node.FunctionExpression { const node = this.createNode(); const isAsync = this.matchContextualKeyword('async'); @@ -3211,9 +3250,7 @@ export class Parser { this.context.isAsync = previousIsAsync; this.context.allowYield = previousAllowYield; - return isAsync - ? this.finalize(node, new Node.AsyncFunctionExpression(id, params, body, isGenerator)) - : this.finalize(node, new Node.FunctionExpression(id, params, body, isGenerator)); + return this.finalize(node, new Node.FunctionExpression(id, params, body, isGenerator, isAsync)); } // https://tc39.github.io/ecma262/#sec-directive-prologues-and-the-use-strict-directive @@ -3276,7 +3313,7 @@ export class Parser { case Token.Keyword: return true; case Token.Punctuator: - return token.value === '['; + return token.value === '[' || token.value === '#'; default: break; } @@ -3296,7 +3333,7 @@ export class Parser { const method = this.parsePropertyMethod(formalParameters); this.context.allowYield = previousAllowYield; - return this.finalize(node, new Node.FunctionExpression(null, formalParameters.params, method, isGenerator)); + return this.finalize(node, new Node.FunctionExpression(null, formalParameters.params, method, isGenerator, false)); } parseSetterMethod(): Node.FunctionExpression { @@ -3314,10 +3351,10 @@ export class Parser { const method = this.parsePropertyMethod(formalParameters); this.context.allowYield = previousAllowYield; - return this.finalize(node, new Node.FunctionExpression(null, formalParameters.params, method, isGenerator)); + return this.finalize(node, new Node.FunctionExpression(null, formalParameters.params, method, isGenerator, false)); } - parseGeneratorMethod(): Node.FunctionExpression { + parseGeneratorMethod(isAsync: boolean): Node.FunctionExpression { const node = this.createNode(); const isGenerator = true; @@ -3329,7 +3366,7 @@ export class Parser { const method = this.parsePropertyMethod(params); this.context.allowYield = previousAllowYield; - return this.finalize(node, new Node.FunctionExpression(null, params.params, method, isGenerator)); + return this.finalize(node, new Node.FunctionExpression(null, params.params, method, isGenerator, isAsync)); } // https://tc39.github.io/ecma262/#sec-generator-function-definitions @@ -3385,7 +3422,7 @@ export class Parser { // https://tc39.github.io/ecma262/#sec-class-definitions - parseClassElement(hasConstructor): Node.Property { + parseClassElement(hasConstructor): Node.PropertyDefinition { let token = this.lookahead; const node = this.createNode(); @@ -3396,13 +3433,19 @@ export class Parser { let method = false; let isStatic = false; let isAsync = false; + let isPrivate = false; let isGenerator = false; if (this.match('*')) { this.nextToken(); } else { computed = this.match('['); - key = this.parseObjectPropertyKey(); + if (this.match('#')) { + isPrivate = true; + this.nextToken(); + token = this.lookahead; + } + key = this.parseObjectPropertyKey(isPrivate); const id = key as Node.Identifier; if (id.name === 'static' && (this.qualifiedPropertyName(this.lookahead) || this.match('*'))) { token = this.lookahead; @@ -3410,8 +3453,18 @@ export class Parser { computed = this.match('['); if (this.match('*')) { this.nextToken(); + if (this.match('#')) { + isPrivate = true; + this.nextToken(); + token = this.lookahead; + } } else { - key = this.parseObjectPropertyKey(); + if (this.match('#')) { + isPrivate = true; + this.nextToken(); + token = this.lookahead; + } + key = this.parseObjectPropertyKey(isPrivate); } } if ((token.type === Token.Identifier) && !this.hasLineTerminator && (token.value === 'async')) { @@ -3424,8 +3477,23 @@ export class Parser { } token = this.lookahead; computed = this.match('['); - key = this.parseObjectPropertyKey(); - if (token.type === Token.Identifier && token.value === 'constructor') { + + if (this.match('*')) { + this.nextToken(); + if (this.match('#')) { + isPrivate = true; + this.nextToken(); + } + } else { + if (this.match('#')) { + isPrivate = true; + this.nextToken(); + token = this.lookahead; + } + + key = this.parseObjectPropertyKey(isPrivate); + } + if (token.type === Token.Identifier && token.value === 'constructor' && !isStatic) { this.tolerateUnexpectedToken(token, Messages.ConstructorIsAsync); } } @@ -3436,21 +3504,39 @@ export class Parser { if (token.type === Token.Identifier) { if (token.value === 'get' && lookaheadPropertyKey) { kind = 'get'; + if (this.match('#')) { + isPrivate = true; + this.nextToken(); + token = this.lookahead; + } computed = this.match('['); - key = this.parseObjectPropertyKey(); + key = this.parseObjectPropertyKey(isPrivate); this.context.allowYield = false; value = this.parseGetterMethod(); } else if (token.value === 'set' && lookaheadPropertyKey) { kind = 'set'; + if (this.match('#')) { + isPrivate = true; + this.nextToken(); + token = this.lookahead; + } computed = this.match('['); - key = this.parseObjectPropertyKey(); + key = this.parseObjectPropertyKey(isPrivate); value = this.parseSetterMethod(); + } else if (!this.match('(')) { + kind = 'property'; + computed = false; + + if (this.match('=')) { + this.nextToken(); + value = this.isolateCoverGrammar(this.parseAssignmentExpression); + } } } else if (token.type === Token.Punctuator && token.value === '*' && lookaheadPropertyKey) { kind = 'init'; computed = this.match('['); - key = this.parseObjectPropertyKey(); - value = this.parseGeneratorMethod(); + key = this.parseObjectPropertyKey(isPrivate); + value = this.parseGeneratorMethod(isAsync); method = true; } @@ -3488,11 +3574,15 @@ export class Parser { } } - return this.finalize(node, new Node.MethodDefinition(key, computed, value, kind, isStatic)); + if (kind === 'property') { + this.consumeSemicolon(); + return this.finalize(node, new Node.PropertyDefinition(key, computed, value, isStatic)); + } else + return this.finalize(node, new Node.MethodDefinition(key, computed, value, kind, isStatic)); } - parseClassElementList(): Node.Property[] { - const body: Node.Property[] = []; + parseClassElementList(): Node.PropertyDefinition[] { + const body: Node.PropertyDefinition[] = []; const hasConstructor = { value: false }; this.expect('{'); diff --git a/src/scanner.ts b/src/scanner.ts index 2a746e029..dcf7fa61c 100644 --- a/src/scanner.ts +++ b/src/scanner.ts @@ -622,6 +622,7 @@ export class Scanner { } break; + case '#': case ')': case ';': case ',': diff --git a/src/syntax.ts b/src/syntax.ts index 6b0ec49cf..a529c0d2e 100644 --- a/src/syntax.ts +++ b/src/syntax.ts @@ -47,6 +47,7 @@ export const Syntax = { ObjectPattern: 'ObjectPattern', Program: 'Program', Property: 'Property', + PrivateIdentifier: 'PrivateIdentifier', RestElement: 'RestElement', ReturnStatement: 'ReturnStatement', SequenceExpression: 'SequenceExpression', diff --git a/src/tokenizer.ts b/src/tokenizer.ts index 6f9fff423..0901bb418 100644 --- a/src/tokenizer.ts +++ b/src/tokenizer.ts @@ -106,7 +106,7 @@ export class Tokenizer { readonly buffer: BufferEntry[]; readonly reader: Reader; - constructor(code: string, config: Config) { + constructor(code: string, config?: Config) { this.errorHandler = new ErrorHandler(); this.errorHandler.tolerant = config ? (typeof config.tolerant === 'boolean' && config.tolerant) : false;