SandpointsGitHook/vendor/github.com/tdewolff/parse/v2/js/ast.go
2021-03-20 23:21:23 +01:00

1228 lines
28 KiB
Go

package js
import (
"bytes"
"fmt"
"strconv"
)
// AST is the full ECMAScript abstract syntax tree.
type AST struct {
Comments [][]byte // first comments in file
BlockStmt // module
}
func (ast *AST) String() string {
s := ""
for i, item := range ast.BlockStmt.List {
if i != 0 {
s += " "
}
s += item.String()
}
return s
}
////////////////////////////////////////////////////////////////
// DeclType specifies the kind of declaration.
type DeclType uint16
// DeclType values.
const (
NoDecl DeclType = iota // undeclared variables
VariableDecl // var
FunctionDecl // function
ArgumentDecl // function and method arguments
LexicalDecl // let, const, class
CatchDecl // catch statement argument
ExprDecl // function expression name or class expression name
)
func (decl DeclType) String() string {
switch decl {
case NoDecl:
return "NoDecl"
case VariableDecl:
return "VariableDecl"
case FunctionDecl:
return "FunctionDecl"
case ArgumentDecl:
return "ArgumentDecl"
case LexicalDecl:
return "LexicalDecl"
case CatchDecl:
return "CatchDecl"
case ExprDecl:
return "ExprDecl"
}
return "Invalid(" + strconv.Itoa(int(decl)) + ")"
}
// Var is a variable, where Decl is the type of declaration and can be var|function for function scoped variables, let|const|class for block scoped variables.
type Var struct {
Data []byte
Link *Var // is set when merging variable uses, as in: {a} {var a} where the first lins to the second
Uses uint16
Decl DeclType
}
// Name returns the variable name.
func (v *Var) Name() []byte {
for v.Link != nil {
v = v.Link
}
return v.Data
}
func (v *Var) String() string {
return string(v.Name())
}
// VarsByUses is sortable by uses in descending order.
type VarsByUses VarArray
func (vs VarsByUses) Len() int {
return len(vs)
}
func (vs VarsByUses) Swap(i, j int) {
vs[i], vs[j] = vs[j], vs[i]
}
func (vs VarsByUses) Less(i, j int) bool {
return vs[i].Uses > vs[j].Uses
}
////////////////////////////////////////////////////////////////
// VarArray is a set of variables in scopes.
type VarArray []*Var
func (vs VarArray) String() string {
s := "["
for i, v := range vs {
if i != 0 {
s += ", "
}
links := 0
for v.Link != nil {
v = v.Link
links++
}
s += fmt.Sprintf("Var{%v %s %v %v}", v.Decl, string(v.Data), links, v.Uses)
}
return s + "]"
}
// Scope is a function or block scope with a list of variables declared and used.
type Scope struct {
Parent, Func *Scope // Parent is nil for global scope, Parent equals Func for function scope
Declared VarArray // Link in Var are always nil
Undeclared VarArray
NumVarDecls uint16 // number of variable declaration statements in a function scope
NumForInit uint16 // offset into Declared to mark variables used in for initializer
NumArguments uint16 // offset into Undeclared to mark variables used in arguments
IsGlobalOrFunc bool
HasWith bool
}
func (s Scope) String() string {
return "Scope{Declared: " + s.Declared.String() + ", Undeclared: " + s.Undeclared.String() + "}"
}
// Declare declares a new variable.
func (s *Scope) Declare(decl DeclType, name []byte) (*Var, bool) {
// refer to new variable for previously undeclared symbols in the current and lower scopes
// this happens in `{ a = 5; } var a` where both a's refer to the same variable
curScope := s
if decl == VariableDecl || decl == FunctionDecl {
// find function scope for var and function declarations
for s != s.Func {
// make sure that `{let i;{var i}}` is an error
if v := s.findDeclared(name, false); v != nil && v.Decl != decl && v.Decl != CatchDecl {
return nil, false
}
s = s.Parent
}
}
if v := s.findDeclared(name, true); v != nil {
// variable already declared, might be an error or a duplicate declaration
if (LexicalDecl <= v.Decl || LexicalDecl <= decl) && v.Decl != ExprDecl {
// redeclaration of let, const, class on an already declared name is an error, except if the declared name is a function expression name
return nil, false
}
if v.Decl == ExprDecl {
v.Decl = decl
}
v.Uses++
for s != curScope {
curScope.addUndeclared(v) // add variable declaration as used variable to the current scope
curScope = curScope.Parent
}
return v, true
}
var v *Var
// reuse variable if previously used, as in: a;var a
if decl != ArgumentDecl { // in case of function f(a=b,b), where the first b is different from the second
for i, uv := range s.Undeclared[s.NumArguments:] {
// no need to evaluate v.Link as v.Data stays the same and Link is nil in the active scope
if 0 < uv.Uses && uv.Decl == NoDecl && bytes.Equal(name, uv.Data) {
// must be NoDecl so that it can't be a var declaration that has been added
v = uv
s.Undeclared = append(s.Undeclared[:int(s.NumArguments)+i], s.Undeclared[int(s.NumArguments)+i+1:]...)
break
}
}
}
if v == nil {
// add variable to the context list and to the scope
v = &Var{name, nil, 0, decl}
} else {
v.Decl = decl
}
v.Uses++
s.Declared = append(s.Declared, v)
for s != curScope {
curScope.addUndeclared(v) // add variable declaration as used variable to the current scope
curScope = curScope.Parent
}
return v, true
}
// Use increments the usage of a variable.
func (s *Scope) Use(name []byte) *Var {
// check if variable is declared in the current scope
v := s.findDeclared(name, false)
if v == nil {
// check if variable is already used before in the current or lower scopes
v = s.findUndeclared(name)
if v == nil {
// add variable to the context list and to the scope's undeclared
v = &Var{name, nil, 0, NoDecl}
s.Undeclared = append(s.Undeclared, v)
}
}
v.Uses++
return v
}
// findDeclared finds a declared variable in the current scope.
func (s *Scope) findDeclared(name []byte, skipForInit bool) *Var {
start := 0
if skipForInit {
// we skip the for initializer for declarations (only has effect for let/const)
start = int(s.NumForInit)
}
// reverse order to find the inner let first in `for(let a in []){let a; {a}}`
for i := len(s.Declared) - 1; start <= i; i-- {
v := s.Declared[i]
// no need to evaluate v.Link as v.Data stays the same, and Link is always nil in Declared
if bytes.Equal(name, v.Data) {
return v
}
}
return nil
}
// findUndeclared finds an undeclared variable in the current and contained scopes.
func (s *Scope) findUndeclared(name []byte) *Var {
for _, v := range s.Undeclared {
// no need to evaluate v.Link as v.Data stays the same and Link is nil in the active scope
if 0 < v.Uses && bytes.Equal(name, v.Data) {
return v
}
}
return nil
}
// add undeclared variable to scope, this is called for the block scope when declaring a var in it
func (s *Scope) addUndeclared(v *Var) {
// don't add undeclared symbol if it's already there
for _, vorig := range s.Undeclared {
if v == vorig {
return
}
}
s.Undeclared = append(s.Undeclared, v) // add variable declaration as used variable to the current scope
}
// MarkForInit marks the declared variables in current scope as for statement initializer to distinguish from declarations in body.
func (s *Scope) MarkForInit() {
s.NumForInit = uint16(len(s.Declared))
}
// MarkArguments marks the undeclared variables in the current scope as function arguments. It ensures different b's in `function f(a=b){var b}`.
func (s *Scope) MarkArguments() {
s.NumArguments = uint16(len(s.Undeclared))
}
// HoistUndeclared copies all undeclared variables of the current scope to the parent scope.
func (s *Scope) HoistUndeclared() {
for i, vorig := range s.Undeclared {
// no need to evaluate vorig.Link as vorig.Data stays the same
if 0 < vorig.Uses && vorig.Decl == NoDecl {
if v := s.Parent.findDeclared(vorig.Data, false); v != nil {
// check if variable is declared in parent scope
v.Uses += vorig.Uses
vorig.Link = v
s.Undeclared[i] = v // point reference to existing var (to avoid many Link chains)
} else if v := s.Parent.findUndeclared(vorig.Data); v != nil {
// check if variable is already used before in parent scope
v.Uses += vorig.Uses
vorig.Link = v
s.Undeclared[i] = v // point reference to existing var (to avoid many Link chains)
} else {
// add variable to the context list and to the scope's undeclared
s.Parent.Undeclared = append(s.Parent.Undeclared, vorig)
}
}
}
}
// UndeclareScope undeclares all declared variables in the current scope and adds them to the parent scope.
// Called when possible arrow func ends up being a parenthesized expression, scope is not further used.
func (s *Scope) UndeclareScope() {
// look if the variable already exists in the parent scope, if so replace the Var pointer in original use
for _, vorig := range s.Declared {
// no need to evaluate vorig.Link as vorig.Data stays the same, and Link is always nil in Declared
// vorig.Uses will be atleast 1
if v := s.Parent.findDeclared(vorig.Data, false); v != nil {
// check if variable has been declared in this scope
v.Uses += vorig.Uses
vorig.Link = v
} else if v := s.Parent.findUndeclared(vorig.Data); v != nil {
// check if variable is already used before in the current or lower scopes
v.Uses += vorig.Uses
vorig.Link = v
} else {
// add variable to the context list and to the scope's undeclared
vorig.Decl = NoDecl
s.Parent.Undeclared = append(s.Parent.Undeclared, vorig)
}
}
s.Declared = s.Declared[:0]
s.Undeclared = s.Undeclared[:0]
}
////////////////////////////////////////////////////////////////
// IStmt is a dummy interface for statements.
type IStmt interface {
String() string
stmtNode()
}
// IBinding is a dummy interface for bindings.
type IBinding interface {
String() string
bindingNode()
}
// IExpr is a dummy interface for expressions.
type IExpr interface {
String() string
exprNode()
}
////////////////////////////////////////////////////////////////
// BlockStmt is a block statement.
type BlockStmt struct {
List []IStmt
Scope
}
func (n BlockStmt) String() string {
s := "Stmt({"
for _, item := range n.List {
s += " " + item.String()
}
return s + " })"
}
// EmptyStmt is an empty statement.
type EmptyStmt struct {
}
func (n EmptyStmt) String() string {
return "Stmt(;)"
}
// ExprStmt is an expression statement.
type ExprStmt struct {
Value IExpr
}
func (n ExprStmt) String() string {
val := n.Value.String()
if val[0] == '(' && val[len(val)-1] == ')' {
return "Stmt" + n.Value.String()
}
return "Stmt(" + n.Value.String() + ")"
}
// IfStmt is an if statement.
type IfStmt struct {
Cond IExpr
Body IStmt
Else IStmt // can be nil
}
func (n IfStmt) String() string {
s := "Stmt(if " + n.Cond.String() + " " + n.Body.String()
if n.Else != nil {
s += " else " + n.Else.String()
}
return s + ")"
}
// DoWhileStmt is a do-while iteration statement.
type DoWhileStmt struct {
Cond IExpr
Body IStmt
}
func (n DoWhileStmt) String() string {
return "Stmt(do " + n.Body.String() + " while " + n.Cond.String() + ")"
}
// WhileStmt is a while iteration statement.
type WhileStmt struct {
Cond IExpr
Body IStmt
}
func (n WhileStmt) String() string {
return "Stmt(while " + n.Cond.String() + " " + n.Body.String() + ")"
}
// ForStmt is a regular for iteration statement.
type ForStmt struct {
Init IExpr // can be nil
Cond IExpr // can be nil
Post IExpr // can be nil
Body BlockStmt
}
func (n ForStmt) String() string {
s := "Stmt(for"
if n.Init != nil {
s += " " + n.Init.String()
}
s += " ;"
if n.Cond != nil {
s += " " + n.Cond.String()
}
s += " ;"
if n.Post != nil {
s += " " + n.Post.String()
}
return s + " " + n.Body.String() + ")"
}
// ForInStmt is a for-in iteration statement.
type ForInStmt struct {
Init IExpr
Value IExpr
Body BlockStmt
}
func (n ForInStmt) String() string {
return "Stmt(for " + n.Init.String() + " in " + n.Value.String() + " " + n.Body.String() + ")"
}
// ForOfStmt is a for-of iteration statement.
type ForOfStmt struct {
Await bool
Init IExpr
Value IExpr
Body BlockStmt
}
func (n ForOfStmt) String() string {
s := "Stmt(for"
if n.Await {
s += " await"
}
return s + " " + n.Init.String() + " of " + n.Value.String() + " " + n.Body.String() + ")"
}
// CaseClause is a case clause or default clause for a switch statement.
type CaseClause struct {
TokenType
Cond IExpr // can be nil
List []IStmt
}
// SwitchStmt is a switch statement.
type SwitchStmt struct {
Init IExpr
List []CaseClause
}
func (n SwitchStmt) String() string {
s := "Stmt(switch " + n.Init.String()
for _, clause := range n.List {
s += " Clause(" + clause.TokenType.String()
if clause.Cond != nil {
s += " " + clause.Cond.String()
}
for _, item := range clause.List {
s += " " + item.String()
}
s += ")"
}
return s + ")"
}
// BranchStmt is a continue or break statement.
type BranchStmt struct {
Type TokenType
Label []byte // can be nil
}
func (n BranchStmt) String() string {
s := "Stmt(" + n.Type.String()
if n.Label != nil {
s += " " + string(n.Label)
}
return s + ")"
}
// ReturnStmt is a return statement.
type ReturnStmt struct {
Value IExpr // can be nil
}
func (n ReturnStmt) String() string {
s := "Stmt(return"
if n.Value != nil {
s += " " + n.Value.String()
}
return s + ")"
}
// WithStmt is a with statement.
type WithStmt struct {
Cond IExpr
Body IStmt
}
func (n WithStmt) String() string {
return "Stmt(with " + n.Cond.String() + " " + n.Body.String() + ")"
}
// LabelledStmt is a labelled statement.
type LabelledStmt struct {
Label []byte
Value IStmt
}
func (n LabelledStmt) String() string {
return "Stmt(" + string(n.Label) + " : " + n.Value.String() + ")"
}
// ThrowStmt is a throw statement.
type ThrowStmt struct {
Value IExpr
}
func (n ThrowStmt) String() string {
return "Stmt(throw " + n.Value.String() + ")"
}
// TryStmt is a try statement.
type TryStmt struct {
Body BlockStmt
Binding IBinding // can be nil
Catch *BlockStmt // can be nil
Finally *BlockStmt // can be nil
}
func (n TryStmt) String() string {
s := "Stmt(try " + n.Body.String()
if n.Catch != nil {
s += " catch"
if n.Binding != nil {
s += " Binding(" + n.Binding.String() + ")"
}
s += " " + n.Catch.String()
}
if n.Finally != nil {
s += " finally " + n.Finally.String()
}
return s + ")"
}
// DebuggerStmt is a debugger statement.
type DebuggerStmt struct {
}
func (n DebuggerStmt) String() string {
return "Stmt(debugger)"
}
// Alias is a name space import or import/export specifier for import/export statements.
type Alias struct {
Name []byte // can be nil
Binding []byte // can be nil
}
func (alias Alias) String() string {
s := ""
if alias.Name != nil {
s += string(alias.Name) + " as "
}
return s + string(alias.Binding)
}
// ImportStmt is an import statement.
type ImportStmt struct {
List []Alias
Default []byte // can be nil
Module []byte
}
func (n ImportStmt) String() string {
s := "Stmt(import"
if n.Default != nil {
s += " " + string(n.Default)
if len(n.List) != 0 {
s += " ,"
}
}
if len(n.List) == 1 && len(n.List[0].Name) == 1 && n.List[0].Name[0] == '*' {
s += " " + n.List[0].String()
} else if 0 < len(n.List) {
s += " {"
for i, item := range n.List {
if i != 0 {
s += " ,"
}
if item.Binding != nil {
s += " " + item.String()
}
}
s += " }"
}
if n.Default != nil || len(n.List) != 0 {
s += " from"
}
return s + " " + string(n.Module) + ")"
}
// ExportStmt is an export statement.
type ExportStmt struct {
List []Alias
Module []byte // can be nil
Default bool
Decl IExpr
}
func (n ExportStmt) String() string {
s := "Stmt(export"
if n.Decl != nil {
if n.Default {
s += " default"
}
return s + " " + n.Decl.String() + ")"
} else if len(n.List) == 1 && (len(n.List[0].Name) == 1 && n.List[0].Name[0] == '*' || n.List[0].Name == nil && len(n.List[0].Binding) == 1 && n.List[0].Binding[0] == '*') {
s += " " + n.List[0].String()
} else if 0 < len(n.List) {
s += " {"
for i, item := range n.List {
if i != 0 {
s += " ,"
}
if item.Binding != nil {
s += " " + item.String()
}
}
s += " }"
}
if n.Module != nil {
s += " from " + string(n.Module)
}
return s + ")"
}
// DirectivePrologueStmt is a string literal at the beginning of a function or module (usually "use strict").
type DirectivePrologueStmt struct {
Value []byte
}
func (n DirectivePrologueStmt) String() string {
return "Stmt(" + string(n.Value) + ")"
}
func (n BlockStmt) stmtNode() {}
func (n EmptyStmt) stmtNode() {}
func (n ExprStmt) stmtNode() {}
func (n IfStmt) stmtNode() {}
func (n DoWhileStmt) stmtNode() {}
func (n WhileStmt) stmtNode() {}
func (n ForStmt) stmtNode() {}
func (n ForInStmt) stmtNode() {}
func (n ForOfStmt) stmtNode() {}
func (n SwitchStmt) stmtNode() {}
func (n BranchStmt) stmtNode() {}
func (n ReturnStmt) stmtNode() {}
func (n WithStmt) stmtNode() {}
func (n LabelledStmt) stmtNode() {}
func (n ThrowStmt) stmtNode() {}
func (n TryStmt) stmtNode() {}
func (n DebuggerStmt) stmtNode() {}
func (n ImportStmt) stmtNode() {}
func (n ExportStmt) stmtNode() {}
func (n DirectivePrologueStmt) stmtNode() {}
////////////////////////////////////////////////////////////////
// PropertyName is a property name for binding properties, method names, and in object literals.
type PropertyName struct {
Literal LiteralExpr
Computed IExpr // can be nil
}
// IsSet returns true is PropertyName is not nil.
func (n PropertyName) IsSet() bool {
return n.IsComputed() || n.Literal.TokenType != ErrorToken
}
// IsComputed returns true if PropertyName is computed.
func (n PropertyName) IsComputed() bool {
return n.Computed != nil
}
// IsIdent returns true if PropertyName equals the given identifier name.
func (n PropertyName) IsIdent(data []byte) bool {
return !n.IsComputed() && n.Literal.TokenType == IdentifierToken && bytes.Equal(data, n.Literal.Data)
}
func (n PropertyName) String() string {
if n.Computed != nil {
val := n.Computed.String()
if val[0] == '(' {
return "[" + val[1:len(val)-1] + "]"
}
return "[" + val + "]"
}
return string(n.Literal.Data)
}
// BindingArray is an array binding pattern.
type BindingArray struct {
List []BindingElement
Rest IBinding // can be nil
}
func (n BindingArray) String() string {
s := "["
for i, item := range n.List {
if i != 0 {
s += ","
}
s += " " + item.String()
}
if n.Rest != nil {
if len(n.List) != 0 {
s += ","
}
s += " ...Binding(" + n.Rest.String() + ")"
}
return s + " ]"
}
// BindingObjectItem is a binding property.
type BindingObjectItem struct {
Key *PropertyName // can be nil
Value BindingElement
}
// BindingObject is an object binding pattern.
type BindingObject struct {
List []BindingObjectItem
Rest *Var // can be nil
}
func (n BindingObject) String() string {
s := "{"
for i, item := range n.List {
if i != 0 {
s += ","
}
if item.Key != nil {
if v, ok := item.Value.Binding.(*Var); !ok || !item.Key.IsIdent(v.Data) {
s += " " + item.Key.String() + ":"
}
}
s += " " + item.Value.String()
}
if n.Rest != nil {
if len(n.List) != 0 {
s += ","
}
s += " ...Binding(" + string(n.Rest.Data) + ")"
}
return s + " }"
}
// BindingElement is a binding element.
type BindingElement struct {
Binding IBinding // can be nil (in case of ellision)
Default IExpr // can be nil
}
func (n BindingElement) String() string {
if n.Binding == nil {
return "Binding()"
}
s := "Binding(" + n.Binding.String()
if n.Default != nil {
s += " = " + n.Default.String()
}
return s + ")"
}
func (v *Var) bindingNode() {}
func (n BindingArray) bindingNode() {}
func (n BindingObject) bindingNode() {}
////////////////////////////////////////////////////////////////
// VarDecl is a variable statement or lexical declaration.
type VarDecl struct {
TokenType
List []BindingElement
}
func (n VarDecl) String() string {
s := "Decl(" + n.TokenType.String()
for _, item := range n.List {
s += " " + item.String()
}
return s + ")"
}
// Params is a list of parameters for functions, methods, and arrow function.
type Params struct {
List []BindingElement
Rest IBinding // can be nil
}
func (n Params) String() string {
s := "Params("
for i, item := range n.List {
if i != 0 {
s += ", "
}
s += item.String()
}
if n.Rest != nil {
if len(n.List) != 0 {
s += ", "
}
s += "...Binding(" + n.Rest.String() + ")"
}
return s + ")"
}
// FuncDecl is an (async) (generator) function declaration or expression.
type FuncDecl struct {
Async bool
Generator bool
Name *Var // can be nil
Params Params
Body BlockStmt
}
func (n FuncDecl) String() string {
s := "Decl("
if n.Async {
s += "async function"
} else {
s += "function"
}
if n.Generator {
s += "*"
}
if n.Name != nil {
s += " " + string(n.Name.Data)
}
return s + " " + n.Params.String() + " " + n.Body.String() + ")"
}
// MethodDecl is a method definition in a class declaration.
type MethodDecl struct {
Static bool
Async bool
Generator bool
Get bool
Set bool
Name PropertyName
Params Params
Body BlockStmt
}
func (n MethodDecl) String() string {
s := ""
if n.Static {
s += " static"
}
if n.Async {
s += " async"
}
if n.Generator {
s += " *"
}
if n.Get {
s += " get"
}
if n.Set {
s += " set"
}
s += " " + n.Name.String() + " " + n.Params.String() + " " + n.Body.String()
return "Method(" + s[1:] + ")"
}
// ClassDecl is a class declaration.
type ClassDecl struct {
Name *Var // can be nil
Extends IExpr // can be nil
Methods []MethodDecl
}
func (n ClassDecl) String() string {
s := "Decl(class"
if n.Name != nil {
s += " " + string(n.Name.Data)
}
if n.Extends != nil {
s += " extends " + n.Extends.String()
}
for _, item := range n.Methods {
s += " " + item.String()
}
return s + ")"
}
func (n VarDecl) stmtNode() {}
func (n FuncDecl) stmtNode() {}
func (n ClassDecl) stmtNode() {}
func (n VarDecl) exprNode() {} // not a real IExpr, used for ForInit and ExportDecl
func (n FuncDecl) exprNode() {}
func (n ClassDecl) exprNode() {}
func (n MethodDecl) exprNode() {} // not a real IExpr, used for ObjectExpression PropertyName
////////////////////////////////////////////////////////////////
// LiteralExpr can be this, null, boolean, numeric, string, or regular expression literals.
type LiteralExpr struct {
TokenType
Data []byte
}
func (n LiteralExpr) String() string {
return string(n.Data)
}
// Element is an array literal element.
type Element struct {
Value IExpr // can be nil
Spread bool
}
// ArrayExpr is an array literal.
type ArrayExpr struct {
List []Element
}
func (n ArrayExpr) String() string {
s := "["
for i, item := range n.List {
if i != 0 {
s += ", "
}
if item.Value != nil {
if item.Spread {
s += "..."
}
s += item.Value.String()
}
}
if 0 < len(n.List) && n.List[len(n.List)-1].Value == nil {
s += ","
}
return s + "]"
}
// Property is a property definition in an object literal.
type Property struct {
// either Name or Spread are set. When Spread is set then Value is AssignmentExpression
// if Init is set then Value is IdentifierReference, otherwise it can also be MethodDefinition
Name *PropertyName // can be nil
Spread bool
Value IExpr
Init IExpr // can be nil
}
func (n Property) String() string {
s := ""
if n.Name != nil {
if v, ok := n.Value.(*Var); !ok || !n.Name.IsIdent(v.Data) {
s += n.Name.String() + ": "
}
} else if n.Spread {
s += "..."
}
s += n.Value.String()
if n.Init != nil {
s += " = " + n.Init.String()
}
return s
}
// ObjectExpr is an object literal.
type ObjectExpr struct {
List []Property
}
func (n ObjectExpr) String() string {
s := "{"
for i, item := range n.List {
if i != 0 {
s += ", "
}
s += item.String()
}
return s + "}"
}
// TemplatePart is a template head or middle.
type TemplatePart struct {
Value []byte
Expr IExpr
}
// TemplateExpr is a template literal or member/call expression, super property, or optional chain with template literal.
type TemplateExpr struct {
Tag IExpr // can be nil
List []TemplatePart
Tail []byte
Prec OpPrec
}
func (n TemplateExpr) String() string {
s := ""
if n.Tag != nil {
s += n.Tag.String()
}
for _, item := range n.List {
s += string(item.Value) + item.Expr.String()
}
return s + string(n.Tail)
}
// GroupExpr is a parenthesized expression.
type GroupExpr struct {
X IExpr
}
func (n GroupExpr) String() string {
return "(" + n.X.String() + ")"
}
// IndexExpr is a member/call expression, super property, or optional chain with an index expression.
type IndexExpr struct {
X IExpr
Y IExpr
Prec OpPrec
}
func (n IndexExpr) String() string {
return "(" + n.X.String() + "[" + n.Y.String() + "])"
}
// DotExpr is a member/call expression, super property, or optional chain with a dot expression.
type DotExpr struct {
X IExpr
Y LiteralExpr
Prec OpPrec
}
func (n DotExpr) String() string {
return "(" + n.X.String() + "." + n.Y.String() + ")"
}
// NewTargetExpr is a new target meta property.
type NewTargetExpr struct {
}
func (n NewTargetExpr) String() string {
return "(new.target)"
}
// ImportMetaExpr is a import meta meta property.
type ImportMetaExpr struct {
}
func (n ImportMetaExpr) String() string {
return "(import.meta)"
}
type Arg struct {
Value IExpr
Rest bool
}
// Args is a list of arguments as used by new and call expressions.
type Args struct {
List []Arg
}
func (n Args) String() string {
s := "("
for i, item := range n.List {
if i != 0 {
s += ", "
}
if item.Rest {
s += "..."
}
s += item.Value.String()
}
return s + ")"
}
// NewExpr is a new expression or new member expression.
type NewExpr struct {
X IExpr
Args *Args // can be nil
}
func (n NewExpr) String() string {
if n.Args != nil {
return "(new " + n.X.String() + n.Args.String() + ")"
}
return "(new " + n.X.String() + ")"
}
// CallExpr is a call expression.
type CallExpr struct {
X IExpr
Args Args
}
func (n CallExpr) String() string {
return "(" + n.X.String() + n.Args.String() + ")"
}
// OptChainExpr is an optional chain.
type OptChainExpr struct {
X IExpr
Y IExpr // can be CallExpr, IndexExpr, LiteralExpr, or TemplateExpr
}
func (n OptChainExpr) String() string {
s := "(" + n.X.String() + "?."
switch y := n.Y.(type) {
case *CallExpr:
return s + y.Args.String() + ")"
case *IndexExpr:
return s + "[" + y.Y.String() + "])"
default:
return s + y.String() + ")"
}
}
// UnaryExpr is an update or unary expression.
type UnaryExpr struct {
Op TokenType
X IExpr
}
func (n UnaryExpr) String() string {
if n.Op == PostIncrToken || n.Op == PostDecrToken {
return "(" + n.X.String() + n.Op.String() + ")"
} else if IsIdentifierName(n.Op) {
return "(" + n.Op.String() + " " + n.X.String() + ")"
}
return "(" + n.Op.String() + n.X.String() + ")"
}
// BinaryExpr is a binary expression.
type BinaryExpr struct {
Op TokenType
X, Y IExpr
}
func (n BinaryExpr) String() string {
if IsIdentifierName(n.Op) {
return "(" + n.X.String() + " " + n.Op.String() + " " + n.Y.String() + ")"
}
return "(" + n.X.String() + n.Op.String() + n.Y.String() + ")"
}
// CondExpr is a conditional expression.
type CondExpr struct {
Cond, X, Y IExpr
}
func (n CondExpr) String() string {
return "(" + n.Cond.String() + " ? " + n.X.String() + " : " + n.Y.String() + ")"
}
// YieldExpr is a yield expression.
type YieldExpr struct {
Generator bool
X IExpr // can be nil
}
func (n YieldExpr) String() string {
if n.X == nil {
return "(yield)"
}
s := "(yield"
if n.Generator {
s += "*"
}
return s + " " + n.X.String() + ")"
}
// ArrowFunc is an (async) arrow function.
type ArrowFunc struct {
Async bool
Params Params
Body BlockStmt
}
func (n ArrowFunc) String() string {
s := "("
if n.Async {
s += "async "
}
return s + n.Params.String() + " => " + n.Body.String() + ")"
}
func (v *Var) exprNode() {}
func (n LiteralExpr) exprNode() {}
func (n ArrayExpr) exprNode() {}
func (n ObjectExpr) exprNode() {}
func (n TemplateExpr) exprNode() {}
func (n GroupExpr) exprNode() {}
func (n DotExpr) exprNode() {}
func (n IndexExpr) exprNode() {}
func (n NewTargetExpr) exprNode() {}
func (n ImportMetaExpr) exprNode() {}
func (n NewExpr) exprNode() {}
func (n CallExpr) exprNode() {}
func (n OptChainExpr) exprNode() {}
func (n UnaryExpr) exprNode() {}
func (n BinaryExpr) exprNode() {}
func (n CondExpr) exprNode() {}
func (n YieldExpr) exprNode() {}
func (n ArrowFunc) exprNode() {}