diff options
| author | Mitja Felicijan <mitja.felicijan@gmail.com> | 2024-10-25 00:47:47 +0200 |
|---|---|---|
| committer | Mitja Felicijan <mitja.felicijan@gmail.com> | 2024-10-25 00:47:47 +0200 |
| commit | c6cc0108ca7738023b45e0eeac0fa2390532dd93 (patch) | |
| tree | 36890e6cd3091bbab8efbe686cc56f467f645bfd /vendor/github.com/tdewolff/parse/v2/js | |
| parent | 0130404a1dc663d4aa68d780c9bcb23a4243e68d (diff) | |
| download | jbmafp-c6cc0108ca7738023b45e0eeac0fa2390532dd93.tar.gz | |
Diffstat (limited to 'vendor/github.com/tdewolff/parse/v2/js')
| -rw-r--r-- | vendor/github.com/tdewolff/parse/v2/js/README.md | 80 | ||||
| -rw-r--r-- | vendor/github.com/tdewolff/parse/v2/js/ast.go | 3884 | ||||
| -rw-r--r-- | vendor/github.com/tdewolff/parse/v2/js/lex.go | 793 | ||||
| -rw-r--r-- | vendor/github.com/tdewolff/parse/v2/js/parse.go | 2292 | ||||
| -rw-r--r-- | vendor/github.com/tdewolff/parse/v2/js/table.go | 142 | ||||
| -rw-r--r-- | vendor/github.com/tdewolff/parse/v2/js/tokentype.go | 404 | ||||
| -rw-r--r-- | vendor/github.com/tdewolff/parse/v2/js/util.go | 38 | ||||
| -rw-r--r-- | vendor/github.com/tdewolff/parse/v2/js/walk.go | 288 |
8 files changed, 7921 insertions, 0 deletions
diff --git a/vendor/github.com/tdewolff/parse/v2/js/README.md b/vendor/github.com/tdewolff/parse/v2/js/README.md new file mode 100644 index 0000000..e1d4007 --- /dev/null +++ b/vendor/github.com/tdewolff/parse/v2/js/README.md @@ -0,0 +1,80 @@ +# JS [](https://pkg.go.dev/github.com/tdewolff/minify/v2/parse/js?tab=doc) + +This package is a JS lexer (ECMAScript 2020) written in [Go][1]. It follows the specification at [ECMAScript 2020 Language Specification](https://tc39.es/ecma262/). The lexer takes an io.Reader and converts it into tokens until the EOF. + +## Installation +Run the following command + + go get -u github.com/tdewolff/parse/v2/js + +or add the following import and run project with `go get` + + import "github.com/tdewolff/parse/v2/js" + +## Lexer +### Usage +The following initializes a new Lexer with io.Reader `r`: +``` go +l := js.NewLexer(parse.NewInput(r)) +``` + +To tokenize until EOF an error, use: +``` go +for { + tt, text := l.Next() + switch tt { + case js.ErrorToken: + // error or EOF set in l.Err() + return + // ... + } +} +``` + +### Regular Expressions +The ECMAScript specification for `PunctuatorToken` (of which the `/` and `/=` symbols) and `RegExpToken` depend on a parser state to differentiate between the two. The lexer will always parse the first token as `/` or `/=` operator, upon which the parser can rescan that token to scan a regular expression using `RegExp()`. + +### Examples +``` go +package main + +import ( + "os" + + "github.com/tdewolff/parse/v2/js" +) + +// Tokenize JS from stdin. +func main() { + l := js.NewLexer(parse.NewInput(os.Stdin)) + for { + tt, text := l.Next() + switch tt { + case js.ErrorToken: + if l.Err() != io.EOF { + fmt.Println("Error on line", l.Line(), ":", l.Err()) + } + return + case js.IdentifierToken: + fmt.Println("Identifier", string(text)) + case js.NumericToken: + fmt.Println("Numeric", string(text)) + // ... + } + } +} +``` + +## Parser +### Usage +The following parses a file and returns an abstract syntax tree (AST). +``` go +ast, err := js.NewParser(parse.NewInputString("if (state == 5) { console.log('In state five'); }")) +``` + +See [ast.go](https://github.com/tdewolff/parse/blob/master/js/ast.go) for all available data structures that can represent the abstact syntax tree. + +## License +Released under the [MIT license](https://github.com/tdewolff/parse/blob/master/LICENSE.md). + +[1]: http://golang.org/ "Go Language" diff --git a/vendor/github.com/tdewolff/parse/v2/js/ast.go b/vendor/github.com/tdewolff/parse/v2/js/ast.go new file mode 100644 index 0000000..92e80d7 --- /dev/null +++ b/vendor/github.com/tdewolff/parse/v2/js/ast.go @@ -0,0 +1,3884 @@ +package js + +import ( + "bytes" + "fmt" + "io" + "strconv" + + "github.com/tdewolff/parse/v2" +) + +var ErrInvalidJSON = fmt.Errorf("invalid JSON") + +type JSONer interface { + JSON(*bytes.Buffer) error +} + +// 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 links to the second, only used for undeclared variables + 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()) +} + +// JS converts the node back to valid JavaScript +func (v Var) JS() string { + return v.String() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (v Var) JSWriteTo(w io.Writer) (i int, err error) { + return w.Write(v.Name()) +} + +// VarsByUses is sortable by uses in descending order. +// TODO: write custom sorter for varsbyuses +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 + Declared VarArray // Link in Var are always nil + Undeclared VarArray + VarDecls []*VarDecl + NumForDecls uint16 // offset into Declared to mark variables used in for statements + NumFuncArgs uint16 // offset into Declared to mark variables used in function arguments + NumArgUses 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 (ArgumentDecl < v.Decl || FunctionDecl < decl) && v.Decl != ExprDecl { + // only allow (v.Decl,decl) of: (var|function|argument,var|function), (expr,*), any other combination is a syntax error + 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.NumArgUses:] { + // 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.NumArgUses)+i], s.Undeclared[int(s.NumArgUses)+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, skipForDeclared bool) *Var { + start := 0 + if skipForDeclared { + // we skip the for initializer for declarations (only has effect for let/const) + start = int(s.NumForDecls) + } + // 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 +} + +// MarkForStmt marks the declared variables in current scope as for statement initializer to distinguish from declarations in body. +func (s *Scope) MarkForStmt() { + s.NumForDecls = uint16(len(s.Declared)) + s.NumArgUses = uint16(len(s.Undeclared)) // ensures for different b's in for(var a in b){let b} +} + +// MarkFuncArgs marks the declared/undeclared variables in the current scope as function arguments. +func (s *Scope) MarkFuncArgs() { + s.NumFuncArgs = uint16(len(s.Declared)) + s.NumArgUses = uint16(len(s.Undeclared)) // ensures different b's in `function f(a=b){var b}`. +} + +// 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] +} + +// Unscope moves all declared variables of the current scope to the parent scope. Undeclared variables are already in the parent scope. +func (s *Scope) Unscope() { + 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 + s.Parent.Declared = append(s.Parent.Declared, vorig) + } + s.Declared = s.Declared[:0] + s.Undeclared = s.Undeclared[:0] +} + +//////////////////////////////////////////////////////////////// + +// INode is an interface for AST nodes +type INode interface { + String() string + JS() string + JSWriteTo(io.Writer) (int, error) +} + +// IStmt is a dummy interface for statements. +type IStmt interface { + INode + stmtNode() +} + +// IBinding is a dummy interface for bindings. +type IBinding interface { + INode + bindingNode() +} + +// IExpr is a dummy interface for expressions. +type IExpr interface { + INode + 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 + " })" +} + +// JS converts the node back to valid JavaScript +func (n BlockStmt) JS() string { + s := "" + if n.Scope.Parent != nil { + s += "{ " + } + for _, item := range n.List { + if _, isEmpty := item.(*EmptyStmt); !isEmpty { + s += item.JS() + "; " + } + } + if n.Scope.Parent != nil { + s += "}" + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n BlockStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + if n.Scope.Parent != nil { + wn, err = w.Write([]byte("{ ")) + i += wn + if err != nil { + return + } + } + for _, item := range n.List { + if _, isEmpty := item.(*EmptyStmt); !isEmpty { + wn, err = item.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte("; ")) + i += wn + if err != nil { + return + } + } + } + if n.Scope.Parent != nil { + wn, err = w.Write([]byte{'}'}) + i += wn + if err != nil { + return + } + } + return +} + +// EmptyStmt is an empty statement. +type EmptyStmt struct { +} + +func (n EmptyStmt) String() string { + return "Stmt(;)" +} + +// JS converts the node back to valid JavaScript +func (n EmptyStmt) JS() string { + return ";" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n EmptyStmt) JSWriteTo(w io.Writer) (i int, err error) { + wn, err := w.Write([]byte{';'}) + i = wn + return +} + +// 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n ExprStmt) JS() string { + return n.Value.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n ExprStmt) JSWriteTo(w io.Writer) (i int, err error) { + return n.Value.JSWriteTo(w) +} + +// 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 + ")" +} + +// JS converts the node back to valid JavaScript +func (n IfStmt) JS() string { + s := "if (" + n.Cond.JS() + ") " + switch n.Body.(type) { + case *BlockStmt: + s += n.Body.JS() + default: + s += "{ " + n.Body.JS() + " }" + } + if n.Else != nil { + switch n.Else.(type) { + case *BlockStmt: + s += " else " + n.Else.JS() + default: + s += " else { " + n.Else.JS() + " }" + } + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n IfStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("if (")) + i += wn + if err != nil { + return + } + wn, err = n.Cond.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(") ")) + i += wn + if err != nil { + return + } + switch n.Body.(type) { + case *BlockStmt: + wn, err = n.Body.JSWriteTo(w) + i += wn + if err != nil { + return + } + default: + wn, err = w.Write([]byte("{ ")) + i += wn + if err != nil { + return + } + wn, err = n.Body.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" }")) + i += wn + if err != nil { + return + } + } + if n.Else != nil { + switch n.Else.(type) { + case *BlockStmt: + wn, err = w.Write([]byte(" else ")) + i += wn + if err != nil { + return + } + wn, err = n.Else.JSWriteTo(w) + i += wn + if err != nil { + return + } + default: + wn, err = w.Write([]byte(" else { ")) + i += wn + if err != nil { + return + } + wn, err = n.Else.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" }")) + i += wn + if err != nil { + return + } + } + } + return +} + +// 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n DoWhileStmt) JS() string { + s := "do " + switch n.Body.(type) { + case *BlockStmt: + s += n.Body.JS() + default: + s += "{ " + n.Body.JS() + " }" + } + return s + " while (" + n.Cond.JS() + ")" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n DoWhileStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("do ")) + i += wn + if err != nil { + return + } + switch n.Body.(type) { + case *BlockStmt: + wn, err = n.Body.JSWriteTo(w) + i += wn + if err != nil { + return + } + default: + wn, err = w.Write([]byte("{ ")) + i += wn + if err != nil { + return + } + wn, err = n.Body.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" }")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte(" while (")) + i += wn + if err != nil { + return + } + wn, err = n.Cond.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(")")) + i += wn + return +} + +// 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n WhileStmt) JS() string { + s := "while (" + n.Cond.JS() + ") " + if n.Body != nil { + s += n.Body.JS() + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n WhileStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("while (")) + i += wn + if err != nil { + return + } + wn, err = n.Cond.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(") ")) + i += wn + if err != nil { + return + } + if n.Body != nil { + wn, err = n.Body.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + return +} + +// 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 v, ok := n.Init.(*VarDecl); !ok && n.Init != nil || ok && len(v.List) != 0 { + 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n ForStmt) JS() string { + s := "for (" + if v, ok := n.Init.(*VarDecl); !ok && n.Init != nil || ok && len(v.List) != 0 { + s += n.Init.JS() + } else { + s += " " + } + s += "; " + if n.Cond != nil { + s += n.Cond.JS() + } + s += "; " + if n.Post != nil { + s += n.Post.JS() + } + return s + ") " + n.Body.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n ForStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("for (")) + i += wn + if err != nil { + return + } + if v, ok := n.Init.(*VarDecl); !ok && n.Init != nil || ok && len(v.List) != 0 { + wn, err = n.Init.JSWriteTo(w) + i += wn + if err != nil { + return + } + } else { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte("; ")) + i += wn + if err != nil { + return + } + if n.Cond != nil { + wn, err = n.Cond.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte("; ")) + i += wn + if err != nil { + return + } + if n.Post != nil { + wn, err = n.Post.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte(") ")) + i += wn + if err != nil { + return + } + wn, err = n.Body.JSWriteTo(w) + i += wn + return +} + +// 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n ForInStmt) JS() string { + return "for (" + n.Init.JS() + " in " + n.Value.JS() + ") " + n.Body.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n ForInStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("for (")) + i += wn + if err != nil { + return + } + wn, err = n.Init.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" in ")) + i += wn + if err != nil { + return + } + wn, err = n.Value.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(") ")) + i += wn + if err != nil { + return + } + wn, err = n.Body.JSWriteTo(w) + i += wn + return +} + +// 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n ForOfStmt) JS() string { + s := "for" + if n.Await { + s += " await" + } + return s + " (" + n.Init.JS() + " of " + n.Value.JS() + ") " + n.Body.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n ForOfStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("for")) + i += wn + if err != nil { + return + } + if n.Await { + wn, err = w.Write([]byte(" await")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte(" (")) + i += wn + if err != nil { + return + } + wn, err = n.Init.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" of ")) + i += wn + if err != nil { + return + } + wn, err = n.Value.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(") ")) + i += wn + if err != nil { + return + } + wn, err = n.Body.JSWriteTo(w) + i += wn + return +} + +// CaseClause is a case clause or default clause for a switch statement. +type CaseClause struct { + TokenType + Cond IExpr // can be nil + List []IStmt +} + +func (n CaseClause) String() string { + s := " Clause(" + n.TokenType.String() + if n.Cond != nil { + s += " " + n.Cond.String() + } + for _, item := range n.List { + s += " " + item.String() + } + return s + ")" +} + +// JS converts the node back to valid JavaScript +func (n CaseClause) JS() string { + s := " " + if n.Cond != nil { + s += "case " + n.Cond.JS() + } else { + s += "default" + } + s += ":" + for _, item := range n.List { + s += " " + item.JS() + ";" + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n CaseClause) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + if n.Cond != nil { + wn, err = w.Write([]byte("case ")) + i += wn + if err != nil { + return + } + wn, err = n.Cond.JSWriteTo(w) + i += wn + if err != nil { + return + } + } else { + wn, err = w.Write([]byte("default")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte(":")) + i += wn + if err != nil { + return + } + for _, item := range n.List { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = item.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(";")) + i += wn + if err != nil { + return + } + } + return +} + +// SwitchStmt is a switch statement. +type SwitchStmt struct { + Init IExpr + List []CaseClause + Scope +} + +func (n SwitchStmt) String() string { + s := "Stmt(switch " + n.Init.String() + for _, clause := range n.List { + s += clause.String() + } + return s + ")" +} + +// JS converts the node back to valid JavaScript +func (n SwitchStmt) JS() string { + s := "switch (" + n.Init.JS() + ") {" + for _, clause := range n.List { + s += clause.JS() + } + return s + " }" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n SwitchStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("switch (")) + i += wn + if err != nil { + return + } + wn, err = n.Init.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(") {")) + i += wn + if err != nil { + return + } + for _, clause := range n.List { + wn, err = clause.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte(" }")) + i += wn + return +} + +// 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 + ")" +} + +// JS converts the node back to valid JavaScript +func (n BranchStmt) JS() string { + s := n.Type.String() + if n.Label != nil { + s += " " + string(n.Label) + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n BranchStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write(n.Type.Bytes()) + i += wn + if err != nil { + return + } + if n.Label != nil { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = w.Write(n.Label) + i += wn + if err != nil { + return + } + } + return +} + +// 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 + ")" +} + +// JS converts the node back to valid JavaScript +func (n ReturnStmt) JS() string { + s := "return" + if n.Value != nil { + s += " " + n.Value.JS() + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n ReturnStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("return")) + i += wn + if err != nil { + return + } + if n.Value != nil { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = n.Value.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + return +} + +// 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n WithStmt) JS() string { + return "with (" + n.Cond.JS() + ") " + n.Body.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n WithStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("with (")) + i += wn + if err != nil { + return + } + wn, err = n.Cond.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(") ")) + i += wn + if err != nil { + return + } + wn, err = n.Body.JSWriteTo(w) + i += wn + return +} + +// 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n LabelledStmt) JS() string { + return string(n.Label) + ": " + n.Value.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n LabelledStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write(n.Label) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(": ")) + i += wn + if err != nil { + return + } + wn, err = n.Value.JSWriteTo(w) + i += wn + return +} + +// ThrowStmt is a throw statement. +type ThrowStmt struct { + Value IExpr +} + +func (n ThrowStmt) String() string { + return "Stmt(throw " + n.Value.String() + ")" +} + +// JS converts the node back to valid JavaScript +func (n ThrowStmt) JS() string { + return "throw " + n.Value.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n ThrowStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("throw ")) + i += wn + if err != nil { + return + } + wn, err = n.Value.JSWriteTo(w) + i += wn + return +} + +// 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 + ")" +} + +// JS converts the node back to valid JavaScript +func (n TryStmt) JS() string { + s := "try " + n.Body.JS() + if n.Catch != nil { + s += " catch" + if n.Binding != nil { + s += "(" + n.Binding.JS() + ")" + } + s += " " + n.Catch.JS() + } + if n.Finally != nil { + s += " finally " + n.Finally.JS() + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n TryStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("try ")) + i += wn + if err != nil { + return + } + wn, err = n.Body.JSWriteTo(w) + i += wn + if err != nil { + return + } + if n.Catch != nil { + wn, err = w.Write([]byte(" catch")) + i += wn + if err != nil { + return + } + if n.Binding != nil { + wn, err = w.Write([]byte("(")) + i += wn + if err != nil { + return + } + wn, err = n.Binding.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(")")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = n.Catch.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + if n.Finally != nil { + wn, err = w.Write([]byte(" finally ")) + i += wn + if err != nil { + return + } + wn, err = n.Finally.JSWriteTo(w) + i += wn + } + return +} + +// DebuggerStmt is a debugger statement. +type DebuggerStmt struct { +} + +func (n DebuggerStmt) String() string { + return "Stmt(debugger)" +} + +// JS converts the node back to valid JavaScript +func (n DebuggerStmt) JS() string { + return "debugger" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n DebuggerStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("debugger")) + i += wn + return +} + +// 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) +} + +// JS converts the node back to valid JavaScript +func (alias Alias) JS() string { + return alias.String() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (alias Alias) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + if alias.Name != nil { + wn, err = w.Write(alias.Name) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" as ")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write(alias.Binding) + i += wn + return +} + +// 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) + ")" +} + +// JS converts the node back to valid JavaScript +func (n ImportStmt) JS() string { + s := "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].JS() + } else if 0 < len(n.List) { + s += " {" + for i, item := range n.List { + if i != 0 { + s += " ," + } + if item.Binding != nil { + s += " " + item.JS() + } + } + s += " }" + } + if n.Default != nil || len(n.List) != 0 { + s += " from" + } + return s + " " + string(n.Module) +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n ImportStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("import")) + i += wn + if err != nil { + return + } + if n.Default != nil { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = w.Write(n.Default) + i += wn + if err != nil { + return + } + if len(n.List) != 0 { + wn, err = w.Write([]byte(" ,")) + i += wn + if err != nil { + return + } + } + } + if len(n.List) == 1 && len(n.List[0].Name) == 1 && n.List[0].Name[0] == '*' { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = n.List[0].JSWriteTo(w) + i += wn + if err != nil { + return + } + } else if 0 < len(n.List) { + wn, err = w.Write([]byte(" {")) + i += wn + if err != nil { + return + } + for j, item := range n.List { + if j != 0 { + wn, err = w.Write([]byte(" ,")) + i += wn + if err != nil { + return + } + } + if item.Binding != nil { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = item.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + } + wn, err = w.Write([]byte(" }")) + i += wn + if err != nil { + return + } + } + if n.Default != nil || len(n.List) != 0 { + wn, err = w.Write([]byte(" from")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = w.Write(n.Module) + i += wn + return +} + +// 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 + ")" +} + +// JS converts the node back to valid JavaScript +func (n ExportStmt) JS() string { + s := "export" + if n.Decl != nil { + if n.Default { + s += " default" + } + return s + " " + n.Decl.JS() + } 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].JS() + } else if 0 < len(n.List) { + s += " {" + for i, item := range n.List { + if i != 0 { + s += " ," + } + if item.Binding != nil { + s += " " + item.JS() + } + } + s += " }" + } + if n.Module != nil { + s += " from " + string(n.Module) + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n ExportStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("export")) + i += wn + if err != nil { + return + } + if n.Decl != nil { + if n.Default { + wn, err = w.Write([]byte(" default")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = n.Decl.JSWriteTo(w) + i += wn + return + } 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] == '*') { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = n.List[0].JSWriteTo(w) + i += wn + if err != nil { + return + } + } else if 0 < len(n.List) { + wn, err = w.Write([]byte(" {")) + i += wn + if err != nil { + return + } + for j, item := range n.List { + if j != 0 { + wn, err = w.Write([]byte(" ,")) + i += wn + if err != nil { + return + } + } + if item.Binding != nil { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = item.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + } + wn, err = w.Write([]byte(" }")) + i += wn + if err != nil { + return + } + } + if n.Module != nil { + wn, err = w.Write([]byte(" from ")) + i += wn + if err != nil { + return + } + wn, err = w.Write(n.Module) + i += wn + if err != nil { + return + } + } + return +} + +// 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) + ")" +} + +// JS converts the node back to valid JavaScript +func (n DirectivePrologueStmt) JS() string { + return string(n.Value) +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n DirectivePrologueStmt) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write(n.Value) + i += wn + return +} + +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) +} + +// JS converts the node back to valid JavaScript +func (n PropertyName) JS() string { + if n.Computed != nil { + return "[" + n.Computed.JS() + "]" + } + return string(n.Literal.Data) +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n PropertyName) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + if n.Computed != nil { + wn, err = w.Write([]byte("[")) + i += wn + if err != nil { + return + } + wn, err = n.Computed.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte("]")) + i += wn + return + } + wn, err = w.Write(n.Literal.Data) + i += wn + return +} + +// 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 + " ]" +} + +// JS converts the node back to valid JavaScript +func (n BindingArray) JS() string { + s := "[" + for i, item := range n.List { + if i != 0 { + s += ", " + } + s += item.JS() + } + if n.Rest != nil { + if len(n.List) != 0 { + s += ", " + } + s += "..." + n.Rest.JS() + } + return s + "]" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n BindingArray) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("[")) + i += wn + if err != nil { + return + } + for j, item := range n.List { + if j != 0 { + wn, err = w.Write([]byte(", ")) + i += wn + if err != nil { + return + } + } + wn, err = item.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + if n.Rest != nil { + if len(n.List) != 0 { + wn, err = w.Write([]byte(", ")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte("...")) + i += wn + if err != nil { + return + } + wn, err = n.Rest.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte("]")) + i += wn + return +} + +// BindingObjectItem is a binding property. +type BindingObjectItem struct { + Key *PropertyName // can be nil + Value BindingElement +} + +func (n BindingObjectItem) String() string { + s := "" + if n.Key != nil { + if v, ok := n.Value.Binding.(*Var); !ok || !n.Key.IsIdent(v.Data) { + s += " " + n.Key.String() + ":" + } + } + return s + " " + n.Value.String() +} + +// JS converts the node back to valid JavaScript +func (n BindingObjectItem) JS() string { + s := "" + if n.Key != nil { + if v, ok := n.Value.Binding.(*Var); !ok || !n.Key.IsIdent(v.Data) { + s += n.Key.JS() + ": " + } + } + return s + n.Value.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n BindingObjectItem) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + if n.Key != nil { + if v, ok := n.Value.Binding.(*Var); !ok || !n.Key.IsIdent(v.Data) { + wn, err = n.Key.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(": ")) + i += wn + if err != nil { + return + } + } + } + wn, err = n.Value.JSWriteTo(w) + i += wn + return +} + +// 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 += "," + } + s += item.String() + } + if n.Rest != nil { + if len(n.List) != 0 { + s += "," + } + s += " ...Binding(" + string(n.Rest.Data) + ")" + } + return s + " }" +} + +// JS converts the node back to valid JavaScript +func (n BindingObject) JS() string { + s := "{" + for i, item := range n.List { + if i != 0 { + s += ", " + } + s += item.JS() + } + if n.Rest != nil { + if len(n.List) != 0 { + s += ", " + } + s += "..." + string(n.Rest.Data) + } + return s + "}" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n BindingObject) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("{")) + i += wn + if err != nil { + return + } + for j, item := range n.List { + if j != 0 { + wn, err = w.Write([]byte(", ")) + i += wn + if err != nil { + return + } + } + wn, err = item.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + if n.Rest != nil { + if len(n.List) != 0 { + wn, err = w.Write([]byte(", ")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte("...")) + i += wn + if err != nil { + return + } + wn, err = w.Write(n.Rest.Data) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte("}")) + i += wn + return +} + +// 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 + ")" +} + +// JS converts the node back to valid JavaScript +func (n BindingElement) JS() string { + if n.Binding == nil { + return "" + } + s := n.Binding.JS() + if n.Default != nil { + s += " = " + n.Default.JS() + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n BindingElement) JSWriteTo(w io.Writer) (i int, err error) { + if n.Binding == nil { + return + } + var wn int + wn, err = n.Binding.JSWriteTo(w) + i += wn + if err != nil { + return + } + if n.Default != nil { + wn, err = w.Write([]byte(" = ")) + i += wn + if err != nil { + return + } + wn, err = n.Default.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + return +} + +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 + Scope *Scope + InFor, InForInOf bool +} + +func (n VarDecl) String() string { + s := "Decl(" + n.TokenType.String() + for _, item := range n.List { + s += " " + item.String() + } + return s + ")" +} + +// JS converts the node back to valid JavaScript +func (n VarDecl) JS() string { + s := n.TokenType.String() + for i, item := range n.List { + if i != 0 { + s += "," + } + s += " " + item.JS() + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n VarDecl) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write(n.TokenType.Bytes()) + i += wn + if err != nil { + return + } + for j, item := range n.List { + if j != 0 { + wn, err = w.Write([]byte(",")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = item.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + return +} + +// 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 + ")" +} + +// JS converts the node back to valid JavaScript +func (n Params) JS() string { + s := "(" + for i, item := range n.List { + if i != 0 { + s += ", " + } + s += item.JS() + } + if n.Rest != nil { + if len(n.List) != 0 { + s += ", " + } + s += "..." + n.Rest.JS() + } + return s + ")" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n Params) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("(")) + i += wn + if err != nil { + return + } + for j, item := range n.List { + if j != 0 { + wn, err = w.Write([]byte(", ")) + i += wn + if err != nil { + return + } + } + wn, err = item.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + if n.Rest != nil { + if len(n.List) != 0 { + wn, err = w.Write([]byte(", ")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte("...")) + i += wn + if err != nil { + return + } + wn, err = n.Rest.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte(")")) + i += wn + return +} + +// 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n FuncDecl) JS() string { + s := "" + 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.JS() + " " + n.Body.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n FuncDecl) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + if n.Async { + wn, err = w.Write([]byte("async function")) + } else { + wn, err = w.Write([]byte("function")) + } + i += wn + if err != nil { + return + } + + if n.Generator { + wn, err = w.Write([]byte("*")) + i += wn + if err != nil { + return + } + } + if n.Name != nil { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = w.Write(n.Name.Data) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = n.Params.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = n.Body.JSWriteTo(w) + i += wn + return +} + +// 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:] + ")" +} + +// JS converts the node back to valid JavaScript +func (n MethodDecl) JS() 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.JS() + " " + n.Params.JS() + " " + n.Body.JS() + return s[1:] +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n MethodDecl) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + if n.Static { + wn, err = w.Write([]byte("static")) + i += wn + if err != nil { + return + } + } + if n.Async { + if wn > 0 { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte("async")) + i += wn + if err != nil { + return + } + } + if n.Generator { + if wn > 0 { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte("*")) + i += wn + if err != nil { + return + } + } + if n.Get { + if wn > 0 { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte("get")) + i += wn + if err != nil { + return + } + } + if n.Set { + if wn > 0 { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte("set")) + i += wn + if err != nil { + return + } + } + if wn > 0 { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + } + wn, err = n.Name.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = n.Params.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = n.Body.JSWriteTo(w) + i += wn + return +} + +// Field is a field definition in a class declaration. +type Field struct { + Static bool + Name PropertyName + Init IExpr +} + +func (n Field) String() string { + s := "Field(" + if n.Static { + s += "static " + } + s += n.Name.String() + if n.Init != nil { + s += " = " + n.Init.String() + } + return s + ")" +} + +// JS converts the node back to valid JavaScript +func (n Field) JS() string { + s := "" + if n.Static { + s += "static " + } + s += n.Name.String() + if n.Init != nil { + s += " = " + n.Init.JS() + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n Field) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + if n.Static { + wn, err = w.Write([]byte("static ")) + i += wn + if err != nil { + return + } + } + wn, err = n.Name.JSWriteTo(w) + i += wn + if err != nil { + return + } + if n.Init != nil { + wn, err = w.Write([]byte(" = ")) + i += wn + if err != nil { + return + } + wn, err = n.Init.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + return +} + +// ClassElement is a class element that is either a static block, a field definition, or a class method +type ClassElement struct { + StaticBlock *BlockStmt // can be nil + Method *MethodDecl // can be nil + Field +} + +func (n ClassElement) String() string { + if n.StaticBlock != nil { + return "Static(" + n.StaticBlock.String() + ")" + } else if n.Method != nil { + return n.Method.String() + } + return n.Field.String() +} + +// JS converts the node back to valid JavaScript +func (n ClassElement) JS() string { + if n.StaticBlock != nil { + return "static " + n.StaticBlock.JS() + } else if n.Method != nil { + return n.Method.JS() + } + return n.Field.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n ClassElement) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + if n.StaticBlock != nil { + wn, err = w.Write([]byte("static ")) + i += wn + if err != nil { + return + } + wn, err = n.StaticBlock.JSWriteTo(w) + i += wn + return + } else if n.Method != nil { + wn, err = n.Method.JSWriteTo(w) + i += wn + return + } + wn, err = n.Field.JSWriteTo(w) + i += wn + return +} + +// ClassDecl is a class declaration. +type ClassDecl struct { + Name *Var // can be nil + Extends IExpr // can be nil + List []ClassElement +} + +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.List { + s += " " + item.String() + } + return s + ")" +} + +// JS converts the node back to valid JavaScript +func (n ClassDecl) JS() string { + s := "class" + if n.Name != nil { + s += " " + string(n.Name.Data) + } + if n.Extends != nil { + s += " extends " + n.Extends.JS() + } + s += " { " + for _, item := range n.List { + s += item.JS() + "; " + } + return s + "}" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n ClassDecl) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("class")) + i += wn + if err != nil { + return + } + if n.Name != nil { + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = w.Write(n.Name.Data) + i += wn + if err != nil { + return + } + } + if n.Extends != nil { + wn, err = w.Write([]byte(" extends ")) + i += wn + if err != nil { + return + } + wn, err = n.Extends.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte(" { ")) + i += wn + if err != nil { + return + } + for _, item := range n.List { + wn, err = item.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte("; ")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte("}")) + i += wn + return +} + +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) +} + +// JS converts the node back to valid JavaScript +func (n LiteralExpr) JS() string { + return string(n.Data) +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n LiteralExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write(n.Data) + i += wn + return +} + +// JSON converts the node back to valid JSON +func (n LiteralExpr) JSON(buf *bytes.Buffer) error { + if n.TokenType == TrueToken || n.TokenType == FalseToken || n.TokenType == NullToken || n.TokenType == DecimalToken { + buf.Write(n.Data) + return nil + } else if n.TokenType == StringToken { + data := n.Data + if n.Data[0] == '\'' { + data = parse.Copy(data) + data = bytes.ReplaceAll(data, []byte(`"`), []byte(`\"`)) + data[0] = '"' + data[len(data)-1] = '"' + } + buf.Write(data) + return nil + } + return ErrInvalidJSON +} + +// Element is an array literal element. +type Element struct { + Value IExpr // can be nil + Spread bool +} + +func (n Element) String() string { + s := "" + if n.Value != nil { + if n.Spread { + s += "..." + } + s += n.Value.String() + } + return s +} + +// JS converts the node back to valid JavaScript +func (n Element) JS() string { + s := "" + if n.Value != nil { + if n.Spread { + s += "..." + } + s += n.Value.JS() + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n Element) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + if n.Value != nil { + if n.Spread { + wn, err = w.Write([]byte("...")) + i += wn + if err != nil { + return + } + } + wn, err = n.Value.JSWriteTo(w) + i += wn + } + return +} + +// 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 + "]" +} + +// JS converts the node back to valid JavaScript +func (n ArrayExpr) JS() string { + s := "[" + for i, item := range n.List { + if i != 0 { + s += ", " + } + if item.Value != nil { + if item.Spread { + s += "..." + } + s += item.Value.JS() + } + } + if 0 < len(n.List) && n.List[len(n.List)-1].Value == nil { + s += "," + } + return s + "]" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n ArrayExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("[")) + i += wn + if err != nil { + return + } + for j, item := range n.List { + if j != 0 { + wn, err = w.Write([]byte(", ")) + i += wn + if err != nil { + return + } + } + if item.Value != nil { + if item.Spread { + wn, err = w.Write([]byte("...")) + i += wn + if err != nil { + return + } + } + wn, err = item.Value.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + } + if 0 < len(n.List) && n.List[len(n.List)-1].Value == nil { + wn, err = w.Write([]byte(",")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte("]")) + i += wn + return +} + +// JSON converts the node back to valid JSON +func (n ArrayExpr) JSON(buf *bytes.Buffer) error { + buf.WriteByte('[') + for i, item := range n.List { + if i != 0 { + buf.WriteString(", ") + } + if item.Value == nil || item.Spread { + return ErrInvalidJSON + } + val, ok := item.Value.(JSONer) + if !ok { + return ErrInvalidJSON + } else if err := val.JSON(buf); err != nil { + return err + } + } + buf.WriteByte(']') + return nil +} + +// 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 +} + +// JS converts the node back to valid JavaScript +func (n Property) JS() string { + s := "" + if n.Name != nil { + if v, ok := n.Value.(*Var); !ok || !n.Name.IsIdent(v.Data) { + s += n.Name.JS() + ": " + } + } else if n.Spread { + s += "..." + } + s += n.Value.JS() + if n.Init != nil { + s += " = " + n.Init.JS() + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n Property) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + if n.Name != nil { + if v, ok := n.Value.(*Var); !ok || !n.Name.IsIdent(v.Data) { + wn, err = n.Name.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(": ")) + i += wn + if err != nil { + return + } + } + } else if n.Spread { + wn, err = w.Write([]byte("...")) + i += wn + if err != nil { + return + } + } + wn, err = n.Value.JSWriteTo(w) + i += wn + if err != nil { + return + } + if n.Init != nil { + wn, err = w.Write([]byte(" = ")) + i += wn + if err != nil { + return + } + wn, err = n.Init.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + return +} + +// JSON converts the node back to valid JSON +func (n Property) JSON(buf *bytes.Buffer) error { + if n.Name == nil || n.Name.Literal.TokenType != StringToken && n.Name.Literal.TokenType != IdentifierToken || n.Spread || n.Init != nil { + return ErrInvalidJSON + } else if n.Name.Literal.TokenType == IdentifierToken { + buf.WriteByte('"') + buf.Write(n.Name.Literal.Data) + buf.WriteByte('"') + } else { + _ = n.Name.Literal.JSON(buf) + } + buf.WriteString(": ") + + val, ok := n.Value.(JSONer) + if !ok { + return ErrInvalidJSON + } else if err := val.JSON(buf); err != nil { + return err + } + return nil +} + +// 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 + "}" +} + +// JS converts the node back to valid JavaScript +func (n ObjectExpr) JS() string { + s := "{" + for i, item := range n.List { + if i != 0 { + s += ", " + } + s += item.JS() + } + return s + "}" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n ObjectExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("{")) + i += wn + if err != nil { + return + } + for j, item := range n.List { + if j != 0 { + wn, err = w.Write([]byte(", ")) + i += wn + if err != nil { + return + } + } + wn, err = item.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte("}")) + i += wn + return +} + +// JSON converts the node back to valid JSON +func (n ObjectExpr) JSON(buf *bytes.Buffer) error { + buf.WriteByte('{') + for i, item := range n.List { + if i != 0 { + buf.WriteString(", ") + } + if err := item.JSON(buf); err != nil { + return err + } + } + buf.WriteByte('}') + return nil +} + +// TemplatePart is a template head or middle. +type TemplatePart struct { + Value []byte + Expr IExpr +} + +func (n TemplatePart) String() string { + return string(n.Value) + n.Expr.String() +} + +// JS converts the node back to valid JavaScript +func (n TemplatePart) JS() string { + return string(n.Value) + n.Expr.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n TemplatePart) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write(n.Value) + i += wn + if err != nil { + return + } + wn, err = n.Expr.JSWriteTo(w) + i += wn + return +} + +// 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 + Optional bool +} + +func (n TemplateExpr) String() string { + s := "" + if n.Tag != nil { + s += n.Tag.String() + if n.Optional { + s += "?." + } + } + for _, item := range n.List { + s += item.String() + } + return s + string(n.Tail) +} + +// JS converts the node back to valid JavaScript +func (n TemplateExpr) JS() string { + s := "" + if n.Tag != nil { + s += n.Tag.JS() + if n.Optional { + s += "?." + } + } + for _, item := range n.List { + s += item.JS() + } + return s + string(n.Tail) +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n TemplateExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + if n.Tag != nil { + wn, err = n.Tag.JSWriteTo(w) + i += wn + if err != nil { + return + } + if n.Optional { + wn, err = w.Write([]byte("?.")) + i += wn + if err != nil { + return + } + } + } + for _, item := range n.List { + wn, err = item.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + wn, err = w.Write(n.Tail) + i += wn + return +} + +// GroupExpr is a parenthesized expression. +type GroupExpr struct { + X IExpr +} + +func (n GroupExpr) String() string { + return "(" + n.X.String() + ")" +} + +// JS converts the node back to valid JavaScript +func (n GroupExpr) JS() string { + return "(" + n.X.JS() + ")" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n GroupExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("(")) + i += wn + if err != nil { + return + } + wn, err = n.X.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(")")) + i += wn + return +} + +// IndexExpr is a member/call expression, super property, or optional chain with an index expression. +type IndexExpr struct { + X IExpr + Y IExpr + Prec OpPrec + Optional bool +} + +func (n IndexExpr) String() string { + if n.Optional { + return "(" + n.X.String() + "?.[" + n.Y.String() + "])" + } + return "(" + n.X.String() + "[" + n.Y.String() + "])" +} + +// JS converts the node back to valid JavaScript +func (n IndexExpr) JS() string { + if n.Optional { + return n.X.JS() + "?.[" + n.Y.JS() + "]" + } + return n.X.JS() + "[" + n.Y.JS() + "]" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n IndexExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = n.X.JSWriteTo(w) + i += wn + if err != nil { + return + } + if n.Optional { + wn, err = w.Write([]byte("?.[")) + i += wn + if err != nil { + return + } + } else { + wn, err = w.Write([]byte("[")) + i += wn + if err != nil { + return + } + } + wn, err = n.Y.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte("]")) + i += wn + return +} + +// DotExpr is a member/call expression, super property, or optional chain with a dot expression. +type DotExpr struct { + X IExpr + Y LiteralExpr + Prec OpPrec + Optional bool +} + +func (n DotExpr) String() string { + if n.Optional { + return "(" + n.X.String() + "?." + n.Y.String() + ")" + } + return "(" + n.X.String() + "." + n.Y.String() + ")" +} + +// JS converts the node back to valid JavaScript +func (n DotExpr) JS() string { + if n.Optional { + return n.X.JS() + "?." + n.Y.JS() + } + return n.X.JS() + "." + n.Y.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n DotExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = n.X.JSWriteTo(w) + i += wn + if err != nil { + return + } + if n.Optional { + wn, err = w.Write([]byte("?.")) + i += wn + if err != nil { + return + } + } else { + wn, err = w.Write([]byte(".")) + i += wn + if err != nil { + return + } + } + wn, err = n.Y.JSWriteTo(w) + i += wn + return +} + +// NewTargetExpr is a new target meta property. +type NewTargetExpr struct { +} + +func (n NewTargetExpr) String() string { + return "(new.target)" +} + +// JS converts the node back to valid JavaScript +func (n NewTargetExpr) JS() string { + return "new.target" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n NewTargetExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("new.target")) + i += wn + return +} + +// ImportMetaExpr is a import meta meta property. +type ImportMetaExpr struct { +} + +func (n ImportMetaExpr) String() string { + return "(import.meta)" +} + +// JS converts the node back to valid JavaScript +func (n ImportMetaExpr) JS() string { + return "import.meta" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n ImportMetaExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("import.meta")) + i += wn + return +} + +type Arg struct { + Value IExpr + Rest bool +} + +func (n Arg) String() string { + s := "" + if n.Rest { + s += "..." + } + return s + n.Value.String() +} + +// JS converts the node back to valid JavaScript +func (n Arg) JS() string { + s := "" + if n.Rest { + s += "..." + } + return s + n.Value.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n Arg) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + if n.Rest { + wn, err = w.Write([]byte("...")) + i += wn + if err != nil { + return + } + } + wn, err = n.Value.JSWriteTo(w) + i += wn + return +} + +// 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 += ", " + } + s += item.String() + } + return s + ")" +} + +// JS converts the node back to valid JavaScript +func (n Args) JS() string { + s := "" + for i, item := range n.List { + if i != 0 { + s += ", " + } + s += item.JS() + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n Args) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + for j, item := range n.List { + if j != 0 { + wn, err = w.Write([]byte(", ")) + i += wn + if err != nil { + return + } + } + wn, err = item.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + return +} + +// 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n NewExpr) JS() string { + if n.Args != nil { + return "new " + n.X.JS() + "(" + n.Args.JS() + ")" + } + + // always use parentheses to prevent errors when chaining e.g. new Date().getTime() + return "new " + n.X.JS() + "()" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n NewExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("new ")) + i += wn + if err != nil { + return + } + wn, err = n.X.JSWriteTo(w) + i += wn + if err != nil { + return + } + if n.Args != nil { + wn, err = w.Write([]byte("(")) + i += wn + if err != nil { + return + } + wn, err = n.Args.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(")")) + i += wn + if err != nil { + return + } + } else { + wn, err = w.Write([]byte("()")) + i += wn + if err != nil { + return + } + } + return +} + +// CallExpr is a call expression. +type CallExpr struct { + X IExpr + Args Args + Optional bool +} + +func (n CallExpr) String() string { + if n.Optional { + return "(" + n.X.String() + "?." + n.Args.String() + ")" + } + return "(" + n.X.String() + n.Args.String() + ")" +} + +// JS converts the node back to valid JavaScript +func (n CallExpr) JS() string { + if n.Optional { + return n.X.JS() + "?.(" + n.Args.JS() + ")" + } + return n.X.JS() + "(" + n.Args.JS() + ")" +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n CallExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = n.X.JSWriteTo(w) + i += wn + if err != nil { + return + } + if n.Optional { + wn, err = w.Write([]byte("?.(")) + i += wn + if err != nil { + return + } + } else { + wn, err = w.Write([]byte("(")) + i += wn + if err != nil { + return + } + } + wn, err = n.Args.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(")")) + i += wn + if err != nil { + return + } + return +} + +// 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n UnaryExpr) JS() string { + if n.Op == PostIncrToken || n.Op == PostDecrToken { + return n.X.JS() + n.Op.String() + } else if IsIdentifierName(n.Op) { + return n.Op.String() + " " + n.X.JS() + } + return n.Op.String() + n.X.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n UnaryExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + if n.Op == PostIncrToken || n.Op == PostDecrToken { + wn, err = n.X.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write(n.Op.Bytes()) + i += wn + return + } else if IsIdentifierName(n.Op) { + wn, err = w.Write(n.Op.Bytes()) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = n.X.JSWriteTo(w) + i += wn + return + } + wn, err = w.Write(n.Op.Bytes()) + i += wn + if err != nil { + return + } + wn, err = n.X.JSWriteTo(w) + i += wn + return +} + +// JSON converts the node back to valid JSON +func (n UnaryExpr) JSON(buf *bytes.Buffer) error { + if lit, ok := n.X.(*LiteralExpr); ok && n.Op == NegToken && lit.TokenType == DecimalToken { + buf.WriteByte('-') + buf.Write(lit.Data) + return nil + } + return ErrInvalidJSON +} + +// 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n BinaryExpr) JS() string { + return n.X.JS() + " " + n.Op.String() + " " + n.Y.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n BinaryExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = n.X.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = w.Write(n.Op.Bytes()) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = n.Y.JSWriteTo(w) + i += wn + return +} + +// 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n CondExpr) JS() string { + return n.Cond.JS() + " ? " + n.X.JS() + " : " + n.Y.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n CondExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = n.Cond.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" ? ")) + i += wn + if err != nil { + return + } + wn, err = n.X.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" : ")) + i += wn + if err != nil { + return + } + wn, err = n.Y.JSWriteTo(w) + i += wn + return +} + +// 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n YieldExpr) JS() string { + if n.X == nil { + return "yield" + } + s := "yield" + if n.Generator { + s += "*" + } + return s + " " + n.X.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n YieldExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + wn, err = w.Write([]byte("yield")) + i += wn + if err != nil { + return + } + if n.X == nil { + return + } + if n.Generator { + wn, err = w.Write([]byte("*")) + i += wn + if err != nil { + return + } + } + wn, err = w.Write([]byte(" ")) + i += wn + if err != nil { + return + } + wn, err = n.X.JSWriteTo(w) + i += wn + return +} + +// 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() + ")" +} + +// JS converts the node back to valid JavaScript +func (n ArrowFunc) JS() string { + s := "" + if n.Async { + s += "async " + } + return s + n.Params.JS() + " => " + n.Body.JS() +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n ArrowFunc) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + if n.Async { + wn, err = w.Write([]byte("async ")) + i += wn + if err != nil { + return + } + } + wn, err = n.Params.JSWriteTo(w) + i += wn + if err != nil { + return + } + wn, err = w.Write([]byte(" => ")) + i += wn + if err != nil { + return + } + wn, err = n.Body.JSWriteTo(w) + i += wn + return +} + +// CommaExpr is a series of comma expressions. +type CommaExpr struct { + List []IExpr +} + +func (n CommaExpr) String() string { + s := "(" + for i, item := range n.List { + if i != 0 { + s += "," + } + s += item.String() + } + return s + ")" +} + +// JS converts the node back to valid JavaScript +func (n CommaExpr) JS() string { + s := "" + for i, item := range n.List { + if i != 0 { + s += "," + } + s += item.JS() + } + return s +} + +// JS converts the node back to valid JavaScript (writes to io.Writer) +func (n CommaExpr) JSWriteTo(w io.Writer) (i int, err error) { + var wn int + for j, item := range n.List { + if j != 0 { + wn, err = w.Write([]byte(",")) + i += wn + if err != nil { + return + } + } + wn, err = item.JSWriteTo(w) + i += wn + if err != nil { + return + } + } + return +} + +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 UnaryExpr) exprNode() {} +func (n BinaryExpr) exprNode() {} +func (n CondExpr) exprNode() {} +func (n YieldExpr) exprNode() {} +func (n ArrowFunc) exprNode() {} +func (n CommaExpr) exprNode() {} diff --git a/vendor/github.com/tdewolff/parse/v2/js/lex.go b/vendor/github.com/tdewolff/parse/v2/js/lex.go new file mode 100644 index 0000000..7d75bf5 --- /dev/null +++ b/vendor/github.com/tdewolff/parse/v2/js/lex.go @@ -0,0 +1,793 @@ +// Package js is an ECMAScript5.1 lexer following the specifications at http://www.ecma-international.org/ecma-262/5.1/. +package js + +import ( + "unicode" + "unicode/utf8" + + "github.com/tdewolff/parse/v2" +) + +var identifierStart = []*unicode.RangeTable{unicode.Lu, unicode.Ll, unicode.Lt, unicode.Lm, unicode.Lo, unicode.Nl, unicode.Other_ID_Start} +var identifierContinue = []*unicode.RangeTable{unicode.Lu, unicode.Ll, unicode.Lt, unicode.Lm, unicode.Lo, unicode.Nl, unicode.Mn, unicode.Mc, unicode.Nd, unicode.Pc, unicode.Other_ID_Continue} + +// IsIdentifierStart returns true if the byte-slice start is the start of an identifier +func IsIdentifierStart(b []byte) bool { + r, _ := utf8.DecodeRune(b) + return r == '$' || r == '\\' || r == '_' || unicode.IsOneOf(identifierStart, r) +} + +// IsIdentifierContinue returns true if the byte-slice start is a continuation of an identifier +func IsIdentifierContinue(b []byte) bool { + r, _ := utf8.DecodeRune(b) + return r == '$' || r == '\\' || r == '\u200C' || r == '\u200D' || unicode.IsOneOf(identifierContinue, r) +} + +// IsIdentifierEnd returns true if the byte-slice end is a start or continuation of an identifier +func IsIdentifierEnd(b []byte) bool { + r, _ := utf8.DecodeLastRune(b) + return r == '$' || r == '\\' || r == '\u200C' || r == '\u200D' || unicode.IsOneOf(identifierContinue, r) +} + +//////////////////////////////////////////////////////////////// + +// Lexer is the state for the lexer. +type Lexer struct { + r *parse.Input + err error + prevLineTerminator bool + prevNumericLiteral bool + level int + templateLevels []int +} + +// NewLexer returns a new Lexer for a given io.Reader. +func NewLexer(r *parse.Input) *Lexer { + return &Lexer{ + r: r, + prevLineTerminator: true, + level: 0, + templateLevels: []int{}, + } +} + +// Err returns the error encountered during lexing, this is often io.EOF but also other errors can be returned. +func (l *Lexer) Err() error { + if l.err != nil { + return l.err + } + return l.r.Err() +} + +// RegExp reparses the input stream for a regular expression. It is assumed that we just received DivToken or DivEqToken with Next(). This function will go back and read that as a regular expression. +func (l *Lexer) RegExp() (TokenType, []byte) { + if 0 < l.r.Offset() && l.r.Peek(-1) == '/' { + l.r.Move(-1) + } else if 1 < l.r.Offset() && l.r.Peek(-1) == '=' && l.r.Peek(-2) == '/' { + l.r.Move(-2) + } else { + l.err = parse.NewErrorLexer(l.r, "expected / or /=") + return ErrorToken, nil + } + l.r.Skip() // trick to set start = pos + + if l.consumeRegExpToken() { + return RegExpToken, l.r.Shift() + } + l.err = parse.NewErrorLexer(l.r, "unexpected EOF or newline") + return ErrorToken, nil +} + +// Next returns the next Token. It returns ErrorToken when an error was encountered. Using Err() one can retrieve the error message. +func (l *Lexer) Next() (TokenType, []byte) { + prevLineTerminator := l.prevLineTerminator + l.prevLineTerminator = false + + prevNumericLiteral := l.prevNumericLiteral + l.prevNumericLiteral = false + + // study on 50x jQuery shows: + // spaces: 20k + // alpha: 16k + // newlines: 14.4k + // operators: 4k + // numbers and dot: 3.6k + // (): 3.4k + // {}: 1.8k + // []: 0.9k + // "': 1k + // semicolon: 2.4k + // colon: 0.8k + // comma: 2.4k + // slash: 1.4k + // `~: almost 0 + + c := l.r.Peek(0) + switch c { + case ' ', '\t', '\v', '\f': + l.r.Move(1) + for l.consumeWhitespace() { + } + l.prevLineTerminator = prevLineTerminator + return WhitespaceToken, l.r.Shift() + case '\n', '\r': + l.r.Move(1) + for l.consumeLineTerminator() { + } + l.prevLineTerminator = true + return LineTerminatorToken, l.r.Shift() + case '>', '=', '!', '+', '*', '%', '&', '|', '^', '~', '?': + if tt := l.consumeOperatorToken(); tt != ErrorToken { + return tt, l.r.Shift() + } + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.': + if tt := l.consumeNumericToken(); tt != ErrorToken || l.r.Pos() != 0 { + l.prevNumericLiteral = true + return tt, l.r.Shift() + } else if c == '.' { + l.r.Move(1) + if l.r.Peek(0) == '.' && l.r.Peek(1) == '.' { + l.r.Move(2) + return EllipsisToken, l.r.Shift() + } + return DotToken, l.r.Shift() + } + case ',': + l.r.Move(1) + return CommaToken, l.r.Shift() + case ';': + l.r.Move(1) + return SemicolonToken, l.r.Shift() + case '(': + l.level++ + l.r.Move(1) + return OpenParenToken, l.r.Shift() + case ')': + l.level-- + l.r.Move(1) + return CloseParenToken, l.r.Shift() + case '/': + if tt := l.consumeCommentToken(); tt != ErrorToken { + return tt, l.r.Shift() + } else if tt := l.consumeOperatorToken(); tt != ErrorToken { + return tt, l.r.Shift() + } + case '{': + l.level++ + l.r.Move(1) + return OpenBraceToken, l.r.Shift() + case '}': + l.level-- + if len(l.templateLevels) != 0 && l.level == l.templateLevels[len(l.templateLevels)-1] { + return l.consumeTemplateToken(), l.r.Shift() + } + l.r.Move(1) + return CloseBraceToken, l.r.Shift() + case ':': + l.r.Move(1) + return ColonToken, l.r.Shift() + case '\'', '"': + return l.consumeStringToken(), l.r.Shift() + case ']': + l.r.Move(1) + return CloseBracketToken, l.r.Shift() + case '[': + l.r.Move(1) + return OpenBracketToken, l.r.Shift() + case '<', '-': + if l.consumeHTMLLikeCommentToken(prevLineTerminator) { + return CommentToken, l.r.Shift() + } else if tt := l.consumeOperatorToken(); tt != ErrorToken { + return tt, l.r.Shift() + } + case '`': + l.templateLevels = append(l.templateLevels, l.level) + return l.consumeTemplateToken(), l.r.Shift() + case '#': + l.r.Move(1) + if l.consumeIdentifierToken() { + return PrivateIdentifierToken, l.r.Shift() + } + return ErrorToken, nil + default: + if l.consumeIdentifierToken() { + if prevNumericLiteral { + l.err = parse.NewErrorLexer(l.r, "unexpected identifier after number") + return ErrorToken, nil + } else if keyword, ok := Keywords[string(l.r.Lexeme())]; ok { + return keyword, l.r.Shift() + } + return IdentifierToken, l.r.Shift() + } + if 0xC0 <= c { + if l.consumeWhitespace() { + for l.consumeWhitespace() { + } + l.prevLineTerminator = prevLineTerminator + return WhitespaceToken, l.r.Shift() + } else if l.consumeLineTerminator() { + for l.consumeLineTerminator() { + } + l.prevLineTerminator = true + return LineTerminatorToken, l.r.Shift() + } + } else if c == 0 && l.r.Err() != nil { + return ErrorToken, nil + } + } + + r, _ := l.r.PeekRune(0) + l.err = parse.NewErrorLexer(l.r, "unexpected %s", parse.Printable(r)) + return ErrorToken, l.r.Shift() +} + +//////////////////////////////////////////////////////////////// + +/* +The following functions follow the specifications at http://www.ecma-international.org/ecma-262/5.1/ +*/ + +func (l *Lexer) consumeWhitespace() bool { + c := l.r.Peek(0) + if c == ' ' || c == '\t' || c == '\v' || c == '\f' { + l.r.Move(1) + return true + } else if 0xC0 <= c { + if r, n := l.r.PeekRune(0); r == '\u00A0' || r == '\uFEFF' || unicode.Is(unicode.Zs, r) { + l.r.Move(n) + return true + } + } + return false +} + +func (l *Lexer) isLineTerminator() bool { + c := l.r.Peek(0) + if c == '\n' || c == '\r' { + return true + } else if c == 0xE2 && l.r.Peek(1) == 0x80 && (l.r.Peek(2) == 0xA8 || l.r.Peek(2) == 0xA9) { + return true + } + return false +} + +func (l *Lexer) consumeLineTerminator() bool { + c := l.r.Peek(0) + if c == '\n' { + l.r.Move(1) + return true + } else if c == '\r' { + if l.r.Peek(1) == '\n' { + l.r.Move(2) + } else { + l.r.Move(1) + } + return true + } else if c == 0xE2 && l.r.Peek(1) == 0x80 && (l.r.Peek(2) == 0xA8 || l.r.Peek(2) == 0xA9) { + l.r.Move(3) + return true + } + return false +} + +func (l *Lexer) consumeDigit() bool { + if c := l.r.Peek(0); c >= '0' && c <= '9' { + l.r.Move(1) + return true + } + return false +} + +func (l *Lexer) consumeHexDigit() bool { + if c := l.r.Peek(0); (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') { + l.r.Move(1) + return true + } + return false +} + +func (l *Lexer) consumeBinaryDigit() bool { + if c := l.r.Peek(0); c == '0' || c == '1' { + l.r.Move(1) + return true + } + return false +} + +func (l *Lexer) consumeOctalDigit() bool { + if c := l.r.Peek(0); c >= '0' && c <= '7' { + l.r.Move(1) + return true + } + return false +} + +func (l *Lexer) consumeUnicodeEscape() bool { + if l.r.Peek(0) != '\\' || l.r.Peek(1) != 'u' { + return false + } + mark := l.r.Pos() + l.r.Move(2) + if c := l.r.Peek(0); c == '{' { + l.r.Move(1) + if l.consumeHexDigit() { + for l.consumeHexDigit() { + } + if c := l.r.Peek(0); c == '}' { + l.r.Move(1) + return true + } + } + l.r.Rewind(mark) + return false + } else if !l.consumeHexDigit() || !l.consumeHexDigit() || !l.consumeHexDigit() || !l.consumeHexDigit() { + l.r.Rewind(mark) + return false + } + return true +} + +func (l *Lexer) consumeSingleLineComment() { + for { + c := l.r.Peek(0) + if c == '\r' || c == '\n' || c == 0 && l.r.Err() != nil { + break + } else if 0xC0 <= c { + if r, _ := l.r.PeekRune(0); r == '\u2028' || r == '\u2029' { + break + } + } + l.r.Move(1) + } +} + +//////////////////////////////////////////////////////////////// + +func (l *Lexer) consumeHTMLLikeCommentToken(prevLineTerminator bool) bool { + c := l.r.Peek(0) + if c == '<' && l.r.Peek(1) == '!' && l.r.Peek(2) == '-' && l.r.Peek(3) == '-' { + // opening HTML-style single line comment + l.r.Move(4) + l.consumeSingleLineComment() + return true + } else if prevLineTerminator && c == '-' && l.r.Peek(1) == '-' && l.r.Peek(2) == '>' { + // closing HTML-style single line comment + // (only if current line didn't contain any meaningful tokens) + l.r.Move(3) + l.consumeSingleLineComment() + return true + } + return false +} + +func (l *Lexer) consumeCommentToken() TokenType { + c := l.r.Peek(1) + if c == '/' { + // single line comment + l.r.Move(2) + l.consumeSingleLineComment() + return CommentToken + } else if c == '*' { + l.r.Move(2) + tt := CommentToken + for { + c := l.r.Peek(0) + if c == '*' && l.r.Peek(1) == '/' { + l.r.Move(2) + break + } else if c == 0 && l.r.Err() != nil { + break + } else if l.consumeLineTerminator() { + l.prevLineTerminator = true + tt = CommentLineTerminatorToken + } else { + l.r.Move(1) + } + } + return tt + } + return ErrorToken +} + +var opTokens = map[byte]TokenType{ + '=': EqToken, + '!': NotToken, + '<': LtToken, + '>': GtToken, + '+': AddToken, + '-': SubToken, + '*': MulToken, + '/': DivToken, + '%': ModToken, + '&': BitAndToken, + '|': BitOrToken, + '^': BitXorToken, + '~': BitNotToken, + '?': QuestionToken, +} + +var opEqTokens = map[byte]TokenType{ + '=': EqEqToken, + '!': NotEqToken, + '<': LtEqToken, + '>': GtEqToken, + '+': AddEqToken, + '-': SubEqToken, + '*': MulEqToken, + '/': DivEqToken, + '%': ModEqToken, + '&': BitAndEqToken, + '|': BitOrEqToken, + '^': BitXorEqToken, +} + +var opOpTokens = map[byte]TokenType{ + '<': LtLtToken, + '+': IncrToken, + '-': DecrToken, + '*': ExpToken, + '&': AndToken, + '|': OrToken, + '?': NullishToken, +} + +var opOpEqTokens = map[byte]TokenType{ + '<': LtLtEqToken, + '*': ExpEqToken, + '&': AndEqToken, + '|': OrEqToken, + '?': NullishEqToken, +} + +func (l *Lexer) consumeOperatorToken() TokenType { + c := l.r.Peek(0) + l.r.Move(1) + if l.r.Peek(0) == '=' { + l.r.Move(1) + if l.r.Peek(0) == '=' && (c == '!' || c == '=') { + l.r.Move(1) + if c == '!' { + return NotEqEqToken + } + return EqEqEqToken + } + return opEqTokens[c] + } else if l.r.Peek(0) == c && (c == '+' || c == '-' || c == '*' || c == '&' || c == '|' || c == '?' || c == '<') { + l.r.Move(1) + if l.r.Peek(0) == '=' && c != '+' && c != '-' { + l.r.Move(1) + return opOpEqTokens[c] + } + return opOpTokens[c] + } else if c == '?' && l.r.Peek(0) == '.' && (l.r.Peek(1) < '0' || l.r.Peek(1) > '9') { + l.r.Move(1) + return OptChainToken + } else if c == '=' && l.r.Peek(0) == '>' { + l.r.Move(1) + return ArrowToken + } else if c == '>' && l.r.Peek(0) == '>' { + l.r.Move(1) + if l.r.Peek(0) == '>' { + l.r.Move(1) + if l.r.Peek(0) == '=' { + l.r.Move(1) + return GtGtGtEqToken + } + return GtGtGtToken + } else if l.r.Peek(0) == '=' { + l.r.Move(1) + return GtGtEqToken + } + return GtGtToken + } + return opTokens[c] +} + +func (l *Lexer) consumeIdentifierToken() bool { + c := l.r.Peek(0) + if identifierStartTable[c] { + l.r.Move(1) + } else if 0xC0 <= c { + if r, n := l.r.PeekRune(0); unicode.IsOneOf(identifierStart, r) { + l.r.Move(n) + } else { + return false + } + } else if !l.consumeUnicodeEscape() { + return false + } + for { + c := l.r.Peek(0) + if identifierTable[c] { + l.r.Move(1) + } else if 0xC0 <= c { + if r, n := l.r.PeekRune(0); r == '\u200C' || r == '\u200D' || unicode.IsOneOf(identifierContinue, r) { + l.r.Move(n) + } else { + break + } + } else if !l.consumeUnicodeEscape() { + break + } + } + return true +} + +func (l *Lexer) consumeNumericSeparator(f func() bool) bool { + if l.r.Peek(0) != '_' { + return false + } + l.r.Move(1) + if !f() { + l.r.Move(-1) + return false + } + return true +} + +func (l *Lexer) consumeNumericToken() TokenType { + // assume to be on 0 1 2 3 4 5 6 7 8 9 . + first := l.r.Peek(0) + if first == '0' { + l.r.Move(1) + if l.r.Peek(0) == 'x' || l.r.Peek(0) == 'X' { + l.r.Move(1) + if l.consumeHexDigit() { + for l.consumeHexDigit() || l.consumeNumericSeparator(l.consumeHexDigit) { + } + return HexadecimalToken + } + l.err = parse.NewErrorLexer(l.r, "invalid hexadecimal number") + return ErrorToken + } else if l.r.Peek(0) == 'b' || l.r.Peek(0) == 'B' { + l.r.Move(1) + if l.consumeBinaryDigit() { + for l.consumeBinaryDigit() || l.consumeNumericSeparator(l.consumeBinaryDigit) { + } + return BinaryToken + } + l.err = parse.NewErrorLexer(l.r, "invalid binary number") + return ErrorToken + } else if l.r.Peek(0) == 'o' || l.r.Peek(0) == 'O' { + l.r.Move(1) + if l.consumeOctalDigit() { + for l.consumeOctalDigit() || l.consumeNumericSeparator(l.consumeOctalDigit) { + } + return OctalToken + } + l.err = parse.NewErrorLexer(l.r, "invalid octal number") + return ErrorToken + } else if l.r.Peek(0) == 'n' { + l.r.Move(1) + return BigIntToken + } else if '0' <= l.r.Peek(0) && l.r.Peek(0) <= '9' { + l.err = parse.NewErrorLexer(l.r, "legacy octal numbers are not supported") + return ErrorToken + } + } else if first != '.' { + for l.consumeDigit() || l.consumeNumericSeparator(l.consumeDigit) { + } + } + // we have parsed a 0 or an integer number + c := l.r.Peek(0) + if c == '.' { + l.r.Move(1) + if l.consumeDigit() { + for l.consumeDigit() || l.consumeNumericSeparator(l.consumeDigit) { + } + c = l.r.Peek(0) + } else if first == '.' { + // number starts with a dot and must be followed by digits + l.r.Move(-1) + return ErrorToken // may be dot or ellipsis + } else { + c = l.r.Peek(0) + } + } else if c == 'n' { + l.r.Move(1) + return BigIntToken + } + if c == 'e' || c == 'E' { + l.r.Move(1) + c = l.r.Peek(0) + if c == '+' || c == '-' { + l.r.Move(1) + } + if !l.consumeDigit() { + l.err = parse.NewErrorLexer(l.r, "invalid number") + return ErrorToken + } + for l.consumeDigit() || l.consumeNumericSeparator(l.consumeDigit) { + } + } + return DecimalToken +} + +func (l *Lexer) consumeStringToken() TokenType { + // assume to be on ' or " + delim := l.r.Peek(0) + l.r.Move(1) + for { + c := l.r.Peek(0) + if c == delim { + l.r.Move(1) + break + } else if c == '\\' { + l.r.Move(1) + if !l.consumeLineTerminator() { + if c := l.r.Peek(0); c == delim || c == '\\' { + l.r.Move(1) + } + } + continue + } else if c == '\n' || c == '\r' || c == 0 && l.r.Err() != nil { + l.err = parse.NewErrorLexer(l.r, "unterminated string literal") + return ErrorToken + } + l.r.Move(1) + } + return StringToken +} + +func (l *Lexer) consumeRegExpToken() bool { + // assume to be on / + l.r.Move(1) + inClass := false + for { + c := l.r.Peek(0) + if !inClass && c == '/' { + l.r.Move(1) + break + } else if c == '[' { + inClass = true + } else if c == ']' { + inClass = false + } else if c == '\\' { + l.r.Move(1) + if l.isLineTerminator() || l.r.Peek(0) == 0 && l.r.Err() != nil { + return false + } + } else if l.isLineTerminator() || c == 0 && l.r.Err() != nil { + return false + } + l.r.Move(1) + } + // flags + for { + c := l.r.Peek(0) + if identifierTable[c] { + l.r.Move(1) + } else if 0xC0 <= c { + if r, n := l.r.PeekRune(0); r == '\u200C' || r == '\u200D' || unicode.IsOneOf(identifierContinue, r) { + l.r.Move(n) + } else { + break + } + } else { + break + } + } + return true +} + +func (l *Lexer) consumeTemplateToken() TokenType { + // assume to be on ` or } when already within template + continuation := l.r.Peek(0) == '}' + l.r.Move(1) + for { + c := l.r.Peek(0) + if c == '`' { + l.templateLevels = l.templateLevels[:len(l.templateLevels)-1] + l.r.Move(1) + if continuation { + return TemplateEndToken + } + return TemplateToken + } else if c == '$' && l.r.Peek(1) == '{' { + l.level++ + l.r.Move(2) + if continuation { + return TemplateMiddleToken + } + return TemplateStartToken + } else if c == '\\' { + l.r.Move(1) + if c := l.r.Peek(0); c != 0 { + l.r.Move(1) + } + continue + } else if c == 0 && l.r.Err() != nil { + l.err = parse.NewErrorLexer(l.r, "unterminated template literal") + return ErrorToken + } + l.r.Move(1) + } +} + +var identifierStartTable = [256]bool{ + // ASCII + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + + false, false, false, false, true, false, false, false, // $ + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + + false, true, true, true, true, true, true, true, // A, B, C, D, E, F, G + true, true, true, true, true, true, true, true, // H, I, J, K, L, M, N, O + true, true, true, true, true, true, true, true, // P, Q, R, S, T, U, V, W + true, true, true, false, false, false, false, true, // X, Y, Z, _ + + false, true, true, true, true, true, true, true, // a, b, c, d, e, f, g + true, true, true, true, true, true, true, true, // h, i, j, k, l, m, n, o + true, true, true, true, true, true, true, true, // p, q, r, s, t, u, v, w + true, true, true, false, false, false, false, false, // x, y, z + + // non-ASCII + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, +} + +var identifierTable = [256]bool{ + // ASCII + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + + false, false, false, false, true, false, false, false, // $ + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, // 0, 1, 2, 3, 4, 5, 6, 7 + true, true, false, false, false, false, false, false, // 8, 9 + + false, true, true, true, true, true, true, true, // A, B, C, D, E, F, G + true, true, true, true, true, true, true, true, // H, I, J, K, L, M, N, O + true, true, true, true, true, true, true, true, // P, Q, R, S, T, U, V, W + true, true, true, false, false, false, false, true, // X, Y, Z, _ + + false, true, true, true, true, true, true, true, // a, b, c, d, e, f, g + true, true, true, true, true, true, true, true, // h, i, j, k, l, m, n, o + true, true, true, true, true, true, true, true, // p, q, r, s, t, u, v, w + true, true, true, false, false, false, false, false, // x, y, z + + // non-ASCII + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, +} diff --git a/vendor/github.com/tdewolff/parse/v2/js/parse.go b/vendor/github.com/tdewolff/parse/v2/js/parse.go new file mode 100644 index 0000000..3d06237 --- /dev/null +++ b/vendor/github.com/tdewolff/parse/v2/js/parse.go @@ -0,0 +1,2292 @@ +package js + +import ( + "bytes" + "errors" + "fmt" + "io" + + "github.com/tdewolff/parse/v2" + "github.com/tdewolff/parse/v2/buffer" +) + +type Options struct { + WhileToFor bool +} + +// Parser is the state for the parser. +type Parser struct { + l *Lexer + o Options + err error + + data []byte + tt TokenType + prevLT bool + inFor bool + await, yield bool + assumeArrowFunc bool + allowDirectivePrologue bool + + stmtLevel int + exprLevel int + + scope *Scope +} + +// Parse returns a JS AST tree of. +func Parse(r *parse.Input, o Options) (*AST, error) { + ast := &AST{} + p := &Parser{ + l: NewLexer(r), + o: o, + tt: WhitespaceToken, // trick so that next() works + await: true, + } + + // process shebang + if r.Peek(0) == '#' && r.Peek(1) == '!' { + r.Move(2) + p.l.consumeSingleLineComment() // consume till end-of-line + ast.Comments = append(ast.Comments, r.Shift()) + } + + p.tt, p.data = p.l.Next() + for p.tt == CommentToken || p.tt == CommentLineTerminatorToken { + ast.Comments = append(ast.Comments, p.data) + p.tt, p.data = p.l.Next() + if p.tt == WhitespaceToken || p.tt == LineTerminatorToken { + p.tt, p.data = p.l.Next() + } + } + if p.tt == WhitespaceToken || p.tt == LineTerminatorToken { + p.next() + } + // prevLT may be wrong but that is not a problem + ast.BlockStmt = p.parseModule() + + if p.err == nil { + p.err = p.l.Err() + } else { + offset := p.l.r.Offset() - len(p.data) + p.err = parse.NewError(buffer.NewReader(p.l.r.Bytes()), offset, p.err.Error()) + } + if p.err == io.EOF { + p.err = nil + } + return ast, p.err +} + +//////////////////////////////////////////////////////////////// + +func (p *Parser) next() { + p.prevLT = false + p.tt, p.data = p.l.Next() + for p.tt == WhitespaceToken || p.tt == LineTerminatorToken || p.tt == CommentToken || p.tt == CommentLineTerminatorToken { + if p.tt == LineTerminatorToken || p.tt == CommentLineTerminatorToken { + p.prevLT = true + } + p.tt, p.data = p.l.Next() + } +} + +func (p *Parser) failMessage(msg string, args ...interface{}) { + if p.err == nil { + p.err = fmt.Errorf(msg, args...) + p.tt = ErrorToken + } +} + +func (p *Parser) fail(in string, expected ...TokenType) { + if p.err == nil { + msg := "unexpected" + if 0 < len(expected) { + msg = "expected" + for i, tt := range expected[:len(expected)-1] { + if 0 < i { + msg += "," + } + msg += " " + tt.String() + "" + } + if 2 < len(expected) { + msg += ", or" + } else if 1 < len(expected) { + msg += " or" + } + msg += " " + expected[len(expected)-1].String() + " instead of" + } + + if p.tt == ErrorToken { + if p.l.Err() == io.EOF { + msg += " EOF" + } else if lexerErr, ok := p.l.Err().(*parse.Error); ok { + msg = lexerErr.Message + } else { + // does not happen + } + } else { + msg += " " + string(p.data) + "" + } + if in != "" { + msg += " in " + in + } + + p.err = errors.New(msg) + p.tt = ErrorToken + } +} + +func (p *Parser) consume(in string, tt TokenType) bool { + if p.tt != tt { + p.fail(in, tt) + return false + } + p.next() + return true +} + +// TODO: refactor +//type ScopeState struct { +// scope *Scope +// async bool +// generator bool +// assumeArrowFunc bool +//} + +func (p *Parser) enterScope(scope *Scope, isFunc bool) *Scope { + // create a new scope object and add it to the parent + parent := p.scope + p.scope = scope + *scope = Scope{ + Parent: parent, + } + if isFunc { + scope.Func = scope + } else if parent != nil { + scope.Func = parent.Func + } + return parent +} + +func (p *Parser) exitScope(parent *Scope) { + p.scope.HoistUndeclared() + p.scope = parent +} + +func (p *Parser) parseModule() (module BlockStmt) { + p.enterScope(&module.Scope, true) + p.allowDirectivePrologue = true + for { + switch p.tt { + case ErrorToken: + return + case ImportToken: + p.next() + if p.tt == OpenParenToken { + // could be an import call expression + left := &LiteralExpr{ImportToken, []byte("import")} + p.exprLevel++ + suffix := p.parseExpressionSuffix(left, OpExpr, OpCall) + p.exprLevel-- + module.List = append(module.List, &ExprStmt{suffix}) + } else { + importStmt := p.parseImportStmt() + module.List = append(module.List, &importStmt) + } + case ExportToken: + exportStmt := p.parseExportStmt() + module.List = append(module.List, &exportStmt) + default: + module.List = append(module.List, p.parseStmt(true)) + } + } +} + +func (p *Parser) parseStmt(allowDeclaration bool) (stmt IStmt) { + p.stmtLevel++ + if 1000 < p.stmtLevel { + p.failMessage("too many nested statements") + return nil + } + + allowDirectivePrologue := p.allowDirectivePrologue + p.allowDirectivePrologue = false + + switch tt := p.tt; tt { + case OpenBraceToken: + stmt = p.parseBlockStmt("block statement") + case ConstToken, VarToken: + if !allowDeclaration && tt == ConstToken { + p.fail("statement") + return + } + p.next() + varDecl := p.parseVarDecl(tt, true) + stmt = varDecl + if !p.prevLT && p.tt != SemicolonToken && p.tt != CloseBraceToken && p.tt != ErrorToken { + if tt == ConstToken { + p.fail("const declaration") + } else { + p.fail("var statement") + } + return + } + case LetToken: + let := p.data + p.next() + if allowDeclaration && (IsIdentifier(p.tt) || p.tt == YieldToken || p.tt == AwaitToken || p.tt == OpenBracketToken || p.tt == OpenBraceToken) { + stmt = p.parseVarDecl(tt, false) + if !p.prevLT && p.tt != SemicolonToken && p.tt != CloseBraceToken && p.tt != ErrorToken { + p.fail("let declaration") + return + } + } else { + // expression + stmt = &ExprStmt{p.parseIdentifierExpression(OpExpr, let)} + if !p.prevLT && p.tt != SemicolonToken && p.tt != CloseBraceToken && p.tt != ErrorToken { + p.fail("expression") + return + } + } + case IfToken: + p.next() + if !p.consume("if statement", OpenParenToken) { + return + } + cond := p.parseExpression(OpExpr) + if !p.consume("if statement", CloseParenToken) { + return + } + body := p.parseStmt(false) + + var elseBody IStmt + if p.tt == ElseToken { + p.next() + elseBody = p.parseStmt(false) + } + stmt = &IfStmt{cond, body, elseBody} + case ContinueToken, BreakToken: + tt := p.tt + p.next() + var label []byte + if !p.prevLT && p.isIdentifierReference(p.tt) { + label = p.data + p.next() + } + stmt = &BranchStmt{tt, label} + case ReturnToken: + p.next() + var value IExpr + if !p.prevLT && p.tt != SemicolonToken && p.tt != CloseBraceToken && p.tt != ErrorToken { + value = p.parseExpression(OpExpr) + } + stmt = &ReturnStmt{value} + case WithToken: + p.next() + if !p.consume("with statement", OpenParenToken) { + return + } + cond := p.parseExpression(OpExpr) + if !p.consume("with statement", CloseParenToken) { + return + } + + p.scope.Func.HasWith = true + stmt = &WithStmt{cond, p.parseStmt(false)} + case DoToken: + stmt = &DoWhileStmt{} + p.next() + body := p.parseStmt(false) + if !p.consume("do-while statement", WhileToken) { + return + } + if !p.consume("do-while statement", OpenParenToken) { + return + } + stmt = &DoWhileStmt{p.parseExpression(OpExpr), body} + if !p.consume("do-while statement", CloseParenToken) { + return + } + case WhileToken: + p.next() + if !p.consume("while statement", OpenParenToken) { + return + } + cond := p.parseExpression(OpExpr) + if !p.consume("while statement", CloseParenToken) { + return + } + body := p.parseStmt(false) + if p.o.WhileToFor { + varDecl := &VarDecl{TokenType: VarToken, Scope: p.scope, InFor: true} + p.scope.Func.VarDecls = append(p.scope.Func.VarDecls, varDecl) + + block, ok := body.(*BlockStmt) + if !ok { + block = &BlockStmt{List: []IStmt{body}} + } + stmt = &ForStmt{varDecl, cond, nil, block} + } else { + stmt = &WhileStmt{cond, body} + } + case ForToken: + p.next() + await := p.await && p.tt == AwaitToken + if await { + p.next() + } + if !p.consume("for statement", OpenParenToken) { + return + } + + body := &BlockStmt{} + parent := p.enterScope(&body.Scope, false) + + var init IExpr + p.inFor = true + if p.tt == VarToken || p.tt == LetToken || p.tt == ConstToken { + tt := p.tt + p.next() + varDecl := p.parseVarDecl(tt, true) + if p.tt != SemicolonToken && (1 < len(varDecl.List) || varDecl.List[0].Default != nil) { + p.fail("for statement") + return + } else if p.tt == SemicolonToken && varDecl.List[0].Default == nil { + // all but the first item were already verified + if _, ok := varDecl.List[0].Binding.(*Var); !ok { + p.fail("for statement") + return + } + } + init = varDecl + } else if p.tt != SemicolonToken { + init = p.parseExpression(OpExpr) + } + p.inFor = false + + if p.tt == SemicolonToken { + var cond, post IExpr + if await { + p.fail("for statement", OfToken) + return + } + p.next() + if p.tt != SemicolonToken { + cond = p.parseExpression(OpExpr) + } + if !p.consume("for statement", SemicolonToken) { + return + } + if p.tt != CloseParenToken { + post = p.parseExpression(OpExpr) + } + if !p.consume("for statement", CloseParenToken) { + return + } + p.scope.MarkForStmt() + if p.tt == OpenBraceToken { + body.List = p.parseStmtList("") + } else if p.tt != SemicolonToken { + body.List = []IStmt{p.parseStmt(false)} + } + if init == nil { + varDecl := &VarDecl{TokenType: VarToken, Scope: p.scope, InFor: true} + p.scope.Func.VarDecls = append(p.scope.Func.VarDecls, varDecl) + init = varDecl + } else if varDecl, ok := init.(*VarDecl); ok { + varDecl.InFor = true + } + stmt = &ForStmt{init, cond, post, body} + } else if p.tt == InToken { + if await { + p.fail("for statement", OfToken) + return + } + p.next() + value := p.parseExpression(OpExpr) + if !p.consume("for statement", CloseParenToken) { + return + } + p.scope.MarkForStmt() + if p.tt == OpenBraceToken { + body.List = p.parseStmtList("") + } else if p.tt != SemicolonToken { + body.List = []IStmt{p.parseStmt(false)} + } + if varDecl, ok := init.(*VarDecl); ok { + varDecl.InForInOf = true + } + stmt = &ForInStmt{init, value, body} + } else if p.tt == OfToken { + p.next() + value := p.parseExpression(OpAssign) + if !p.consume("for statement", CloseParenToken) { + return + } + p.scope.MarkForStmt() + if p.tt == OpenBraceToken { + body.List = p.parseStmtList("") + } else if p.tt != SemicolonToken { + body.List = []IStmt{p.parseStmt(false)} + } + if varDecl, ok := init.(*VarDecl); ok { + varDecl.InForInOf = true + } + stmt = &ForOfStmt{await, init, value, body} + } else { + p.fail("for statement", InToken, OfToken, SemicolonToken) + return + } + p.exitScope(parent) + case SwitchToken: + p.next() + if !p.consume("switch statement", OpenParenToken) { + return + } + init := p.parseExpression(OpExpr) + if !p.consume("switch statement", CloseParenToken) { + return + } + + // case block + if !p.consume("switch statement", OpenBraceToken) { + return + } + + switchStmt := &SwitchStmt{Init: init} + parent := p.enterScope(&switchStmt.Scope, false) + for { + if p.tt == ErrorToken { + p.fail("switch statement") + return + } else if p.tt == CloseBraceToken { + p.next() + break + } + + clause := p.tt + var list IExpr + if p.tt == CaseToken { + p.next() + list = p.parseExpression(OpExpr) + } else if p.tt == DefaultToken { + p.next() + } else { + p.fail("switch statement", CaseToken, DefaultToken) + return + } + if !p.consume("switch statement", ColonToken) { + return + } + + var stmts []IStmt + for p.tt != CaseToken && p.tt != DefaultToken && p.tt != CloseBraceToken && p.tt != ErrorToken { + stmts = append(stmts, p.parseStmt(true)) + } + switchStmt.List = append(switchStmt.List, CaseClause{clause, list, stmts}) + } + p.exitScope(parent) + stmt = switchStmt + case FunctionToken: + if !allowDeclaration { + p.fail("statement") + return + } + stmt = p.parseFuncDecl() + case AsyncToken: // async function + if !allowDeclaration { + p.fail("statement") + return + } + async := p.data + p.next() + if p.tt == FunctionToken && !p.prevLT { + stmt = p.parseAsyncFuncDecl() + } else { + // expression + stmt = &ExprStmt{p.parseAsyncExpression(OpExpr, async)} + if !p.prevLT && p.tt != SemicolonToken && p.tt != CloseBraceToken && p.tt != ErrorToken { + p.fail("expression") + return + } + } + case ClassToken: + if !allowDeclaration { + p.fail("statement") + return + } + stmt = p.parseClassDecl() + case ThrowToken: + p.next() + var value IExpr + if !p.prevLT { + value = p.parseExpression(OpExpr) + } + stmt = &ThrowStmt{value} + case TryToken: + p.next() + body := p.parseBlockStmt("try statement") + var binding IBinding + var catch, finally *BlockStmt + if p.tt == CatchToken { + p.next() + catch = &BlockStmt{} + parent := p.enterScope(&catch.Scope, false) + if p.tt == OpenParenToken { + p.next() + binding = p.parseBinding(CatchDecl) // local to block scope of catch + if !p.consume("try-catch statement", CloseParenToken) { + return + } + } + catch.List = p.parseStmtList("try-catch statement") + p.exitScope(parent) + } else if p.tt != FinallyToken { + p.fail("try statement", CatchToken, FinallyToken) + return + } + if p.tt == FinallyToken { + p.next() + finally = p.parseBlockStmt("try-finally statement") + } + stmt = &TryStmt{body, binding, catch, finally} + case DebuggerToken: + p.next() + stmt = &DebuggerStmt{} + case SemicolonToken, ErrorToken: + stmt = &EmptyStmt{} + default: + if p.isIdentifierReference(p.tt) { + // labelled statement or expression + label := p.data + p.next() + if p.tt == ColonToken { + p.next() + stmt = &LabelledStmt{label, p.parseStmt(true)} // allows illegal async function, generator function, let, const, or class declarations + } else { + // expression + stmt = &ExprStmt{p.parseIdentifierExpression(OpExpr, label)} + if !p.prevLT && p.tt != SemicolonToken && p.tt != CloseBraceToken && p.tt != ErrorToken { + p.fail("expression") + return + } + } + } else { + // expression + stmt = &ExprStmt{p.parseExpression(OpExpr)} + if !p.prevLT && p.tt != SemicolonToken && p.tt != CloseBraceToken && p.tt != ErrorToken { + p.fail("expression") + return + } else if lit, ok := stmt.(*ExprStmt).Value.(*LiteralExpr); ok && allowDirectivePrologue && lit.TokenType == StringToken && len(lit.Data) == 12 && bytes.Equal(lit.Data[1:11], []byte("use strict")) { + stmt = &DirectivePrologueStmt{lit.Data} + p.allowDirectivePrologue = true + } + } + } + if p.tt == SemicolonToken { + p.next() + } + p.stmtLevel-- + return +} + +func (p *Parser) parseStmtList(in string) (list []IStmt) { + if !p.consume(in, OpenBraceToken) { + return + } + for { + if p.tt == ErrorToken { + p.fail("") + return + } else if p.tt == CloseBraceToken { + p.next() + break + } + list = append(list, p.parseStmt(true)) + } + return +} + +func (p *Parser) parseBlockStmt(in string) (blockStmt *BlockStmt) { + blockStmt = &BlockStmt{} + parent := p.enterScope(&blockStmt.Scope, false) + blockStmt.List = p.parseStmtList(in) + p.exitScope(parent) + return +} + +func (p *Parser) parseImportStmt() (importStmt ImportStmt) { + // assume we're passed import + if p.tt == StringToken { + importStmt.Module = p.data + p.next() + } else { + if IsIdentifier(p.tt) || p.tt == YieldToken { + importStmt.Default = p.data + p.next() + if p.tt == CommaToken { + p.next() + } + } + if p.tt == MulToken { + star := p.data + p.next() + if !p.consume("import statement", AsToken) { + return + } + if !IsIdentifier(p.tt) && p.tt != YieldToken { + p.fail("import statement", IdentifierToken) + return + } + importStmt.List = []Alias{Alias{star, p.data}} + p.next() + } else if p.tt == OpenBraceToken { + p.next() + for IsIdentifierName(p.tt) || p.tt == StringToken { + tt := p.tt + var name, binding []byte = nil, p.data + p.next() + if p.tt == AsToken { + p.next() + if !IsIdentifier(p.tt) && p.tt != YieldToken { + p.fail("import statement", IdentifierToken) + return + } + name = binding + binding = p.data + p.next() + } else if !IsIdentifier(tt) && tt != YieldToken || tt == StringToken { + p.fail("import statement", IdentifierToken, StringToken) + return + } + importStmt.List = append(importStmt.List, Alias{name, binding}) + if p.tt == CommaToken { + p.next() + if p.tt == CloseBraceToken { + importStmt.List = append(importStmt.List, Alias{}) + break + } + } + } + if !p.consume("import statement", CloseBraceToken) { + return + } + } + if importStmt.Default == nil && len(importStmt.List) == 0 { + p.fail("import statement", StringToken, IdentifierToken, MulToken, OpenBraceToken) + return + } + + if !p.consume("import statement", FromToken) { + return + } + if p.tt != StringToken { + p.fail("import statement", StringToken) + return + } + importStmt.Module = p.data + p.next() + } + if p.tt == SemicolonToken { + p.next() + } + return +} + +func (p *Parser) parseExportStmt() (exportStmt ExportStmt) { + // assume we're at export + p.next() + if p.tt == MulToken || p.tt == OpenBraceToken { + if p.tt == MulToken { + star := p.data + p.next() + if p.tt == AsToken { + p.next() + if !IsIdentifierName(p.tt) && p.tt != StringToken { + p.fail("export statement", IdentifierToken, StringToken) + return + } + exportStmt.List = []Alias{Alias{star, p.data}} + p.next() + } else { + exportStmt.List = []Alias{Alias{nil, star}} + } + if p.tt != FromToken { + p.fail("export statement", FromToken) + return + } + } else { + p.next() + for IsIdentifierName(p.tt) || p.tt == StringToken { + var name, binding []byte = nil, p.data + p.next() + if p.tt == AsToken { + p.next() + if !IsIdentifierName(p.tt) && p.tt != StringToken { + p.fail("export statement", IdentifierToken, StringToken) + return + } + name = binding + binding = p.data + p.next() + } + exportStmt.List = append(exportStmt.List, Alias{name, binding}) + if p.tt == CommaToken { + p.next() + if p.tt == CloseBraceToken { + exportStmt.List = append(exportStmt.List, Alias{}) + break + } + } + } + if !p.consume("export statement", CloseBraceToken) { + return + } + } + if p.tt == FromToken { + p.next() + if p.tt != StringToken { + p.fail("export statement", StringToken) + return + } + exportStmt.Module = p.data + p.next() + } + } else if p.tt == VarToken || p.tt == ConstToken || p.tt == LetToken { + tt := p.tt + p.next() + exportStmt.Decl = p.parseVarDecl(tt, false) + } else if p.tt == FunctionToken { + exportStmt.Decl = p.parseFuncDecl() + } else if p.tt == AsyncToken { // async function + p.next() + if p.tt != FunctionToken || p.prevLT { + p.fail("export statement", FunctionToken) + return + } + exportStmt.Decl = p.parseAsyncFuncDecl() + } else if p.tt == ClassToken { + exportStmt.Decl = p.parseClassDecl() + } else if p.tt == DefaultToken { + exportStmt.Default = true + p.next() + if p.tt == FunctionToken { + exportStmt.Decl = p.parseFuncDeclDefault() + } else if p.tt == AsyncToken { // async function or async arrow function + async := p.data + p.next() + if p.tt == FunctionToken && !p.prevLT { + exportStmt.Decl = p.parseAsyncFuncDeclDefault() + } else { + // expression + exportStmt.Decl = p.parseAsyncExpression(OpExpr, async) + } + } else if p.tt == ClassToken { + exportStmt.Decl = p.parseClassDeclDefault() + } else { + exportStmt.Decl = p.parseExpression(OpAssign) + } + } else { + p.fail("export statement", MulToken, OpenBraceToken, VarToken, LetToken, ConstToken, FunctionToken, AsyncToken, ClassToken, DefaultToken) + return + } + if p.tt == SemicolonToken { + p.next() + } + return +} + +func (p *Parser) parseVarDecl(tt TokenType, canBeHoisted bool) (varDecl *VarDecl) { + // assume we're past var, let or const + varDecl = &VarDecl{ + TokenType: tt, + Scope: p.scope, + } + declType := LexicalDecl + if tt == VarToken { + declType = VariableDecl + if canBeHoisted { + p.scope.Func.VarDecls = append(p.scope.Func.VarDecls, varDecl) + } + } + for { + // binding element, var declaration in for-in or for-of can never have a default + var bindingElement BindingElement + parentInFor := p.inFor + p.inFor = false + bindingElement.Binding = p.parseBinding(declType) + p.inFor = parentInFor + if p.tt == EqToken { + p.next() + bindingElement.Default = p.parseExpression(OpAssign) + } else if _, ok := bindingElement.Binding.(*Var); !ok && (!p.inFor || 0 < len(varDecl.List)) { + p.fail("var statement", EqToken) + return + } else if tt == ConstToken && (!p.inFor || p.inFor && p.tt != OfToken && p.tt != InToken) { + p.fail("const statement", EqToken) + } + + varDecl.List = append(varDecl.List, bindingElement) + if p.tt == CommaToken { + p.next() + } else { + break + } + } + return +} + +func (p *Parser) parseFuncParams(in string) (params Params) { + if !p.consume(in, OpenParenToken) { + return + } + + for p.tt != CloseParenToken && p.tt != ErrorToken { + if p.tt == EllipsisToken { + // binding rest element + p.next() + params.Rest = p.parseBinding(ArgumentDecl) + p.consume(in, CloseParenToken) + return + } + params.List = append(params.List, p.parseBindingElement(ArgumentDecl)) + if p.tt != CommaToken { + break + } + p.next() + } + if p.tt != CloseParenToken { + p.fail(in) + return + } + p.next() + + // mark undeclared vars as arguments in `function f(a=b){var b}` where the b's are different vars + p.scope.MarkFuncArgs() + return +} + +func (p *Parser) parseFuncDecl() (funcDecl *FuncDecl) { + return p.parseAnyFunc(false, false, false) +} + +func (p *Parser) parseFuncDeclDefault() (funcDecl *FuncDecl) { + return p.parseAnyFunc(false, true, false) +} + +func (p *Parser) parseAsyncFuncDecl() (funcDecl *FuncDecl) { + return p.parseAnyFunc(true, false, false) +} + +func (p *Parser) parseAsyncFuncDeclDefault() (funcDecl *FuncDecl) { + return p.parseAnyFunc(true, true, false) +} + +func (p *Parser) parseFuncExpr() (funcDecl *FuncDecl) { + return p.parseAnyFunc(false, false, true) +} + +func (p *Parser) parseAsyncFuncExpr() (funcDecl *FuncDecl) { + return p.parseAnyFunc(true, false, true) +} + +func (p *Parser) parseAnyFunc(async, exportDefault, expr bool) (funcDecl *FuncDecl) { + // assume we're at function + p.next() + funcDecl = &FuncDecl{} + funcDecl.Async = async + funcDecl.Generator = p.tt == MulToken + if funcDecl.Generator { + p.next() + } + var ok bool + var name []byte + if expr && (IsIdentifier(p.tt) || p.tt == YieldToken || p.tt == AwaitToken) || !expr && p.isIdentifierReference(p.tt) { + name = p.data + if !expr { + funcDecl.Name, ok = p.scope.Declare(FunctionDecl, p.data) + if !ok { + p.failMessage("identifier %s has already been declared", string(p.data)) + return + } + } + p.next() + } else if !expr && !exportDefault { + p.fail("function declaration", IdentifierToken) + return + } else if p.tt != OpenParenToken { + p.fail("function declaration", IdentifierToken, OpenParenToken) + return + } + parent := p.enterScope(&funcDecl.Body.Scope, true) + parentAwait, parentYield := p.await, p.yield + p.await, p.yield = funcDecl.Async, funcDecl.Generator + + if expr && name != nil { + funcDecl.Name, _ = p.scope.Declare(ExprDecl, name) // cannot fail + } + funcDecl.Params = p.parseFuncParams("function declaration") + p.allowDirectivePrologue = true + funcDecl.Body.List = p.parseStmtList("function declaration") + + p.await, p.yield = parentAwait, parentYield + p.exitScope(parent) + return +} + +func (p *Parser) parseClassDecl() (classDecl *ClassDecl) { + return p.parseAnyClass(false, false) +} + +func (p *Parser) parseClassDeclDefault() (classDecl *ClassDecl) { + return p.parseAnyClass(true, false) +} + +func (p *Parser) parseClassExpr() (classDecl *ClassDecl) { + return p.parseAnyClass(false, true) +} + +func (p *Parser) parseAnyClass(exportDefault, expr bool) (classDecl *ClassDecl) { + // assume we're at class + p.next() + classDecl = &ClassDecl{} + if IsIdentifier(p.tt) || p.tt == YieldToken || p.tt == AwaitToken { + if !expr { + var ok bool + classDecl.Name, ok = p.scope.Declare(LexicalDecl, p.data) + if !ok { + p.failMessage("identifier %s has already been declared", string(p.data)) + return + } + } else { + //classDecl.Name, ok = p.scope.Declare(ExprDecl, p.data) // classes do not register vars + classDecl.Name = &Var{p.data, nil, 1, ExprDecl} + } + p.next() + } else if !expr && !exportDefault { + p.fail("class declaration", IdentifierToken) + return + } + if p.tt == ExtendsToken { + p.next() + classDecl.Extends = p.parseExpression(OpLHS) + } + + if !p.consume("class declaration", OpenBraceToken) { + return + } + for { + if p.tt == ErrorToken { + p.fail("class declaration") + return + } else if p.tt == SemicolonToken { + p.next() + continue + } else if p.tt == CloseBraceToken { + p.next() + break + } + + classDecl.List = append(classDecl.List, p.parseClassElement()) + } + return +} + +func (p *Parser) parseClassElement() ClassElement { + method := &MethodDecl{} + var data []byte // either static, async, get, or set + if p.tt == StaticToken { + method.Static = true + data = p.data + p.next() + if p.tt == OpenBraceToken { + return ClassElement{StaticBlock: p.parseBlockStmt("class static block")} + } + } + if p.tt == MulToken { + method.Generator = true + p.next() + } else if p.tt == AsyncToken { + data = p.data + p.next() + if !p.prevLT { + method.Async = true + if p.tt == MulToken { + method.Generator = true + data = nil + p.next() + } + } + } else if p.tt == GetToken { + method.Get = true + data = p.data + p.next() + } else if p.tt == SetToken { + method.Set = true + data = p.data + p.next() + } + + isField := false + if data != nil && p.tt == OpenParenToken { + // (static) method name is: static, async, get, or set + method.Name.Literal = LiteralExpr{IdentifierToken, data} + if method.Async || method.Get || method.Set { + method.Async = false + method.Get = false + method.Set = false + } else { + method.Static = false + } + } else if data != nil && (p.tt == EqToken || p.tt == SemicolonToken || p.tt == CloseBraceToken) { + // (static) field name is: static, async, get, or set + method.Name.Literal = LiteralExpr{IdentifierToken, data} + if !method.Async && !method.Get && !method.Set { + method.Static = false + } + isField = true + } else { + if p.tt == PrivateIdentifierToken { + method.Name.Literal = LiteralExpr{p.tt, p.data} + p.next() + } else { + method.Name = p.parsePropertyName("method or field definition") + } + if (data == nil || method.Static) && p.tt != OpenParenToken { + isField = true + } + } + + if isField { + var init IExpr + if p.tt == EqToken { + p.next() + init = p.parseExpression(OpAssign) + } + return ClassElement{Field: Field{Static: method.Static, Name: method.Name, Init: init}} + } + + parent := p.enterScope(&method.Body.Scope, true) + parentAwait, parentYield := p.await, p.yield + p.await, p.yield = method.Async, method.Generator + + method.Params = p.parseFuncParams("method definition") + p.allowDirectivePrologue = true + method.Body.List = p.parseStmtList("method definition") + + p.await, p.yield = parentAwait, parentYield + p.exitScope(parent) + return ClassElement{Method: method} +} + +func (p *Parser) parsePropertyName(in string) (propertyName PropertyName) { + if IsIdentifierName(p.tt) { + propertyName.Literal = LiteralExpr{IdentifierToken, p.data} + p.next() + } else if p.tt == StringToken { + // reinterpret string as identifier or number if we can, except for empty strings + if isIdent := AsIdentifierName(p.data[1 : len(p.data)-1]); isIdent { + propertyName.Literal = LiteralExpr{IdentifierToken, p.data[1 : len(p.data)-1]} + } else if isNum := AsDecimalLiteral(p.data[1 : len(p.data)-1]); isNum { + propertyName.Literal = LiteralExpr{DecimalToken, p.data[1 : len(p.data)-1]} + } else { + propertyName.Literal = LiteralExpr{p.tt, p.data} + } + p.next() + } else if IsNumeric(p.tt) { + propertyName.Literal = LiteralExpr{p.tt, p.data} + p.next() + } else if p.tt == OpenBracketToken { + p.next() + propertyName.Computed = p.parseExpression(OpAssign) + if !p.consume(in, CloseBracketToken) { + return + } + } else { + p.fail(in, IdentifierToken, StringToken, NumericToken, OpenBracketToken) + return + } + return +} + +func (p *Parser) parseBindingElement(decl DeclType) (bindingElement BindingElement) { + // binding element + bindingElement.Binding = p.parseBinding(decl) + if p.tt == EqToken { + p.next() + bindingElement.Default = p.parseExpression(OpAssign) + } + return +} + +func (p *Parser) parseBinding(decl DeclType) (binding IBinding) { + // binding identifier or binding pattern + if p.isIdentifierReference(p.tt) { + var ok bool + binding, ok = p.scope.Declare(decl, p.data) + if !ok { + p.failMessage("identifier %s has already been declared", string(p.data)) + return + } + p.next() + } else if p.tt == OpenBracketToken { + p.next() + array := BindingArray{} + if p.tt == CommaToken { + array.List = append(array.List, BindingElement{}) + } + last := 0 + for p.tt != CloseBracketToken { + // elision + for p.tt == CommaToken { + p.next() + if p.tt == CommaToken { + array.List = append(array.List, BindingElement{}) + } + } + // binding rest element + if p.tt == EllipsisToken { + p.next() + array.Rest = p.parseBinding(decl) + if p.tt != CloseBracketToken { + p.fail("array binding pattern", CloseBracketToken) + return + } + break + } else if p.tt == CloseBracketToken { + array.List = array.List[:last] + break + } + + array.List = append(array.List, p.parseBindingElement(decl)) + last = len(array.List) + + if p.tt != CommaToken && p.tt != CloseBracketToken { + p.fail("array binding pattern", CommaToken, CloseBracketToken) + return + } + } + p.next() // always CloseBracketToken + binding = &array + } else if p.tt == OpenBraceToken { + p.next() + object := BindingObject{} + for p.tt != CloseBraceToken { + // binding rest property + if p.tt == EllipsisToken { + p.next() + if !p.isIdentifierReference(p.tt) { + p.fail("object binding pattern", IdentifierToken) + return + } + var ok bool + object.Rest, ok = p.scope.Declare(decl, p.data) + if !ok { + p.failMessage("identifier %s has already been declared", string(p.data)) + return + } + p.next() + if p.tt != CloseBraceToken { + p.fail("object binding pattern", CloseBraceToken) + return + } + break + } + + item := BindingObjectItem{} + if p.isIdentifierReference(p.tt) { + name := p.data + item.Key = &PropertyName{LiteralExpr{IdentifierToken, p.data}, nil} + p.next() + if p.tt == ColonToken { + // property name + : + binding element + p.next() + item.Value = p.parseBindingElement(decl) + } else { + // single name binding + var ok bool + item.Key.Literal.Data = parse.Copy(item.Key.Literal.Data) // copy so that renaming doesn't rename the key + item.Value.Binding, ok = p.scope.Declare(decl, name) + if !ok { + p.failMessage("identifier %s has already been declared", string(name)) + return + } + if p.tt == EqToken { + p.next() + item.Value.Default = p.parseExpression(OpAssign) + } + } + } else { + propertyName := p.parsePropertyName("object binding pattern") + item.Key = &propertyName + if !p.consume("object binding pattern", ColonToken) { + return + } + item.Value = p.parseBindingElement(decl) + } + object.List = append(object.List, item) + + if p.tt == CommaToken { + p.next() + } else if p.tt != CloseBraceToken { + p.fail("object binding pattern", CommaToken, CloseBraceToken) + return + } + } + p.next() // always CloseBracketToken + binding = &object + } else { + p.fail("binding") + return + } + return +} + +func (p *Parser) parseArrayLiteral() (array ArrayExpr) { + // assume we're on [ + p.next() + prevComma := true + for { + if p.tt == ErrorToken { + p.fail("expression") + return + } else if p.tt == CloseBracketToken { + p.next() + break + } else if p.tt == CommaToken { + if prevComma { + array.List = append(array.List, Element{}) + } + prevComma = true + p.next() + } else { + spread := p.tt == EllipsisToken + if spread { + p.next() + } + array.List = append(array.List, Element{p.parseAssignmentExpression(), spread}) + prevComma = false + if spread && p.tt != CloseBracketToken { + p.assumeArrowFunc = false + } + } + } + return +} + +func (p *Parser) parseObjectLiteral() (object ObjectExpr) { + // assume we're on { + p.next() + for { + if p.tt == ErrorToken { + p.fail("object literal", CloseBraceToken) + return + } else if p.tt == CloseBraceToken { + p.next() + break + } + + property := Property{} + if p.tt == EllipsisToken { + p.next() + property.Spread = true + property.Value = p.parseAssignmentExpression() + if _, isIdent := property.Value.(*Var); !isIdent || p.tt != CloseBraceToken { + p.assumeArrowFunc = false + } + } else { + // try to parse as MethodDefinition, otherwise fall back to PropertyName:AssignExpr or IdentifierReference + var data []byte + method := MethodDecl{} + if p.tt == MulToken { + p.next() + method.Generator = true + } else if p.tt == AsyncToken { + data = p.data + p.next() + if !p.prevLT { + method.Async = true + if p.tt == MulToken { + p.next() + method.Generator = true + data = nil + } + } else { + method.Name.Literal = LiteralExpr{IdentifierToken, data} + data = nil + } + } else if p.tt == GetToken { + data = p.data + p.next() + method.Get = true + } else if p.tt == SetToken { + data = p.data + p.next() + method.Set = true + } + + // PropertyName + if data != nil && !method.Generator && (p.tt == EqToken || p.tt == CommaToken || p.tt == CloseBraceToken || p.tt == ColonToken || p.tt == OpenParenToken) { + method.Name.Literal = LiteralExpr{IdentifierToken, data} + method.Async = false + method.Get = false + method.Set = false + } else if !method.Name.IsSet() { // did not parse async [LT] + method.Name = p.parsePropertyName("object literal") + if !method.Name.IsSet() { + return + } + } + + if p.tt == OpenParenToken { + // MethodDefinition + parent := p.enterScope(&method.Body.Scope, true) + parentAwait, parentYield := p.await, p.yield + p.await, p.yield = method.Async, method.Generator + + method.Params = p.parseFuncParams("method definition") + method.Body.List = p.parseStmtList("method definition") + + p.await, p.yield = parentAwait, parentYield + p.exitScope(parent) + property.Value = &method + p.assumeArrowFunc = false + } else if p.tt == ColonToken { + // PropertyName : AssignmentExpression + p.next() + property.Name = &method.Name + property.Value = p.parseAssignmentExpression() + } else if method.Name.IsComputed() || !p.isIdentifierReference(method.Name.Literal.TokenType) { + p.fail("object literal", ColonToken, OpenParenToken) + return + } else { + // IdentifierReference (= AssignmentExpression)? + name := method.Name.Literal.Data + method.Name.Literal.Data = parse.Copy(method.Name.Literal.Data) // copy so that renaming doesn't rename the key + property.Name = &method.Name // set key explicitly so after renaming the original is still known + if p.assumeArrowFunc { + var ok bool + property.Value, ok = p.scope.Declare(ArgumentDecl, name) + if !ok { + property.Value = p.scope.Use(name) + p.assumeArrowFunc = false + } + } else { + property.Value = p.scope.Use(name) + } + if p.tt == EqToken { + p.next() + parentAssumeArrowFunc := p.assumeArrowFunc + p.assumeArrowFunc = false + property.Init = p.parseExpression(OpAssign) + p.assumeArrowFunc = parentAssumeArrowFunc + } + } + } + object.List = append(object.List, property) + if p.tt == CommaToken { + p.next() + } else if p.tt != CloseBraceToken { + p.fail("object literal") + return + } + } + return +} + +func (p *Parser) parseTemplateLiteral(precLeft OpPrec) (template TemplateExpr) { + // assume we're on 'Template' or 'TemplateStart' + template.Prec = OpMember + if precLeft < OpMember { + template.Prec = OpCall + } + for p.tt == TemplateStartToken || p.tt == TemplateMiddleToken { + tpl := p.data + p.next() + template.List = append(template.List, TemplatePart{tpl, p.parseExpression(OpExpr)}) + } + if p.tt != TemplateToken && p.tt != TemplateEndToken { + p.fail("template literal", TemplateToken) + return + } + template.Tail = p.data + p.next() // TemplateEndToken + return +} + +func (p *Parser) parseArguments() (args Args) { + // assume we're on ( + p.next() + args.List = make([]Arg, 0, 4) + for { + rest := p.tt == EllipsisToken + if rest { + p.next() + } + + if p.tt == CloseParenToken || p.tt == ErrorToken { + break + } + args.List = append(args.List, Arg{ + Value: p.parseExpression(OpAssign), + Rest: rest, + }) + if p.tt == CommaToken { + p.next() + } + } + p.consume("arguments", CloseParenToken) + return +} + +func (p *Parser) parseAsyncArrowFunc() (arrowFunc *ArrowFunc) { + // expect we're at Identifier or Yield or ( + arrowFunc = &ArrowFunc{} + parent := p.enterScope(&arrowFunc.Body.Scope, true) + parentAwait, parentYield := p.await, p.yield + p.await, p.yield = true, false + + if IsIdentifier(p.tt) || !p.yield && p.tt == YieldToken { + ref, _ := p.scope.Declare(ArgumentDecl, p.data) // cannot fail + p.next() + arrowFunc.Params.List = []BindingElement{{Binding: ref}} + } else { + arrowFunc.Params = p.parseFuncParams("arrow function") + + // could be CallExpression of: async(params) + if p.tt != ArrowToken { + // TODO? + } + } + + p.await, p.yield = true, parentYield + arrowFunc.Async = true + arrowFunc.Body.List = p.parseArrowFuncBody() + + p.await, p.yield = parentAwait, parentYield + p.exitScope(parent) + return +} + +func (p *Parser) parseIdentifierArrowFunc(v *Var) (arrowFunc *ArrowFunc) { + // expect we're at => + arrowFunc = &ArrowFunc{} + parent := p.enterScope(&arrowFunc.Body.Scope, true) + parentAwait, parentYield := p.await, p.yield + + if 1 < v.Uses { + v.Uses-- + v, _ = p.scope.Declare(ArgumentDecl, parse.Copy(v.Data)) // cannot fail + } else { + // if v.Uses==1 it must be undeclared and be the last added + p.scope.Parent.Undeclared = p.scope.Parent.Undeclared[:len(p.scope.Parent.Undeclared)-1] + v.Decl = ArgumentDecl + p.scope.Declared = append(p.scope.Declared, v) + } + + p.await = false + arrowFunc.Params.List = []BindingElement{{v, nil}} + arrowFunc.Body.List = p.parseArrowFuncBody() + + p.await, p.yield = parentAwait, parentYield + p.exitScope(parent) + return +} + +func (p *Parser) parseArrowFuncBody() (list []IStmt) { + // expect we're at arrow + if p.tt != ArrowToken { + p.fail("arrow function", ArrowToken) + return + } else if p.prevLT { + p.fail("expression") + return + } + p.next() + + // mark undeclared vars as arguments in `function f(a=b){var b}` where the b's are different vars + p.scope.MarkFuncArgs() + + if p.tt == OpenBraceToken { + parentInFor := p.inFor + p.inFor = false + p.yield = false + p.allowDirectivePrologue = true + list = p.parseStmtList("arrow function") + p.inFor = parentInFor + } else { + list = []IStmt{&ReturnStmt{p.parseExpression(OpAssign)}} + } + return +} + +func (p *Parser) parseIdentifierExpression(prec OpPrec, ident []byte) IExpr { + var left IExpr + left = p.scope.Use(ident) + return p.parseExpressionSuffix(left, prec, OpPrimary) +} + +func (p *Parser) parseAsyncExpression(prec OpPrec, async []byte) IExpr { + // assume we're at a token after async + var left IExpr + precLeft := OpPrimary + if !p.prevLT && p.tt == FunctionToken { + // primary expression + left = p.parseAsyncFuncExpr() + } else if !p.prevLT && prec <= OpAssign && (p.tt == OpenParenToken || IsIdentifier(p.tt) || !p.yield && p.tt == YieldToken || p.tt == AwaitToken) { + // async arrow function expression + if p.tt == AwaitToken { + p.fail("arrow function") + return nil + } else if p.tt == OpenParenToken { + return p.parseParenthesizedExpressionOrArrowFunc(prec, async) + } + left = p.parseAsyncArrowFunc() + precLeft = OpAssign + } else { + left = p.scope.Use(async) + } + return p.parseExpressionSuffix(left, prec, precLeft) +} + +// parseExpression parses an expression that has a precedence of prec or higher. +func (p *Parser) parseExpression(prec OpPrec) IExpr { + p.exprLevel++ + if 1000 < p.exprLevel { + p.failMessage("too many nested expressions") + return nil + } + + // reparse input if we have / or /= as the beginning of a new expression, this should be a regular expression! + if p.tt == DivToken || p.tt == DivEqToken { + p.tt, p.data = p.l.RegExp() + if p.tt == ErrorToken { + p.fail("regular expression") + return nil + } + } + + var left IExpr + precLeft := OpPrimary + + if IsIdentifier(p.tt) && p.tt != AsyncToken { + left = p.scope.Use(p.data) + p.next() + suffix := p.parseExpressionSuffix(left, prec, precLeft) + p.exprLevel-- + return suffix + } else if IsNumeric(p.tt) { + left = &LiteralExpr{p.tt, p.data} + p.next() + suffix := p.parseExpressionSuffix(left, prec, precLeft) + p.exprLevel-- + return suffix + } + + switch tt := p.tt; tt { + case StringToken, ThisToken, NullToken, TrueToken, FalseToken, RegExpToken: + left = &LiteralExpr{p.tt, p.data} + p.next() + case OpenBracketToken: + parentInFor := p.inFor + p.inFor = false + array := p.parseArrayLiteral() + left = &array + p.inFor = parentInFor + case OpenBraceToken: + parentInFor := p.inFor + p.inFor = false + object := p.parseObjectLiteral() + left = &object + p.inFor = parentInFor + case OpenParenToken: + // parenthesized expression or arrow parameter list + if OpAssign < prec { + // must be a parenthesized expression + p.next() + parentInFor := p.inFor + p.inFor = false + left = &GroupExpr{p.parseExpression(OpExpr)} + p.inFor = parentInFor + if !p.consume("expression", CloseParenToken) { + return nil + } + break + } + suffix := p.parseParenthesizedExpressionOrArrowFunc(prec, nil) + p.exprLevel-- + return suffix + case NotToken, BitNotToken, TypeofToken, VoidToken, DeleteToken: + if OpUnary < prec { + p.fail("expression") + return nil + } + p.next() + left = &UnaryExpr{tt, p.parseExpression(OpUnary)} + precLeft = OpUnary + case AddToken: + if OpUnary < prec { + p.fail("expression") + return nil + } + p.next() + left = &UnaryExpr{PosToken, p.parseExpression(OpUnary)} + precLeft = OpUnary + case SubToken: + if OpUnary < prec { + p.fail("expression") + return nil + } + p.next() + left = &UnaryExpr{NegToken, p.parseExpression(OpUnary)} + precLeft = OpUnary + case IncrToken: + if OpUpdate < prec { + p.fail("expression") + return nil + } + p.next() + left = &UnaryExpr{PreIncrToken, p.parseExpression(OpUnary)} + precLeft = OpUnary + case DecrToken: + if OpUpdate < prec { + p.fail("expression") + return nil + } + p.next() + left = &UnaryExpr{PreDecrToken, p.parseExpression(OpUnary)} + precLeft = OpUnary + case AwaitToken: + // either accepted as IdentifierReference or as AwaitExpression + if p.await && prec <= OpUnary { + p.next() + left = &UnaryExpr{tt, p.parseExpression(OpUnary)} + precLeft = OpUnary + } else if p.await { + p.fail("expression") + return nil + } else { + left = p.scope.Use(p.data) + p.next() + } + case NewToken: + p.next() + if p.tt == DotToken { + p.next() + if !p.consume("new.target expression", TargetToken) { + return nil + } + left = &NewTargetExpr{} + precLeft = OpMember + } else { + newExpr := &NewExpr{p.parseExpression(OpNew), nil} + if p.tt == OpenParenToken { + args := p.parseArguments() + if len(args.List) != 0 { + newExpr.Args = &args + } + precLeft = OpMember + } else { + precLeft = OpNew + } + left = newExpr + } + case ImportToken: + // OpMember < prec does never happen + left = &LiteralExpr{p.tt, p.data} + p.next() + if p.tt == DotToken { + p.next() + if !p.consume("import.meta expression", MetaToken) { + return nil + } + left = &ImportMetaExpr{} + precLeft = OpMember + } else if p.tt != OpenParenToken { + p.fail("import expression", OpenParenToken) + return nil + } else if OpCall < prec { + p.fail("expression") + return nil + } else { + precLeft = OpCall + } + case SuperToken: + // OpMember < prec does never happen + left = &LiteralExpr{p.tt, p.data} + p.next() + if OpCall < prec && p.tt != DotToken && p.tt != OpenBracketToken { + p.fail("super expression", OpenBracketToken, DotToken) + return nil + } else if p.tt != DotToken && p.tt != OpenBracketToken && p.tt != OpenParenToken { + p.fail("super expression", OpenBracketToken, OpenParenToken, DotToken) + return nil + } + if OpCall < prec { + precLeft = OpMember + } else { + precLeft = OpCall + } + case YieldToken: + // either accepted as IdentifierReference or as YieldExpression + if p.yield && prec <= OpAssign { + // YieldExpression + p.next() + yieldExpr := YieldExpr{} + if !p.prevLT { + yieldExpr.Generator = p.tt == MulToken + if yieldExpr.Generator { + p.next() + yieldExpr.X = p.parseExpression(OpAssign) + } else if p.tt != CloseBraceToken && p.tt != CloseBracketToken && p.tt != CloseParenToken && p.tt != ColonToken && p.tt != CommaToken && p.tt != SemicolonToken { + yieldExpr.X = p.parseExpression(OpAssign) + } + } + left = &yieldExpr + precLeft = OpAssign + } else if p.yield { + p.fail("expression") + return nil + } else { + left = p.scope.Use(p.data) + p.next() + } + case AsyncToken: + async := p.data + p.next() + left = p.parseAsyncExpression(prec, async) + case ClassToken: + parentInFor := p.inFor + p.inFor = false + left = p.parseClassExpr() + p.inFor = parentInFor + case FunctionToken: + parentInFor := p.inFor + p.inFor = false + left = p.parseFuncExpr() + p.inFor = parentInFor + case TemplateToken, TemplateStartToken: + parentInFor := p.inFor + p.inFor = false + template := p.parseTemplateLiteral(precLeft) + left = &template + p.inFor = parentInFor + default: + p.fail("expression") + return nil + } + suffix := p.parseExpressionSuffix(left, prec, precLeft) + p.exprLevel-- + return suffix +} + +func (p *Parser) parseExpressionSuffix(left IExpr, prec, precLeft OpPrec) IExpr { + for i := 0; ; i++ { + if 1000 < p.exprLevel+i { + p.failMessage("too many nested expressions") + return nil + } + + switch tt := p.tt; tt { + case EqToken, MulEqToken, DivEqToken, ModEqToken, ExpEqToken, AddEqToken, SubEqToken, LtLtEqToken, GtGtEqToken, GtGtGtEqToken, BitAndEqToken, BitXorEqToken, BitOrEqToken, AndEqToken, OrEqToken, NullishEqToken: + if OpAssign < prec { + return left + } else if precLeft < OpLHS { + p.fail("expression") + return nil + } + p.next() + left = &BinaryExpr{tt, left, p.parseExpression(OpAssign)} + precLeft = OpAssign + case LtToken, LtEqToken, GtToken, GtEqToken, InToken, InstanceofToken: + if OpCompare < prec || p.inFor && tt == InToken { + return left + } else if precLeft < OpCompare { + // can only fail after a yield or arrow function expression + p.fail("expression") + return nil + } + p.next() + left = &BinaryExpr{tt, left, p.parseExpression(OpShift)} + precLeft = OpCompare + case EqEqToken, NotEqToken, EqEqEqToken, NotEqEqToken: + if OpEquals < prec { + return left + } else if precLeft < OpEquals { + // can only fail after a yield or arrow function expression + p.fail("expression") + return nil + } + p.next() + left = &BinaryExpr{tt, left, p.parseExpression(OpCompare)} + precLeft = OpEquals + case AndToken: + if OpAnd < prec { + return left + } else if precLeft < OpAnd { + p.fail("expression") + return nil + } + p.next() + left = &BinaryExpr{tt, left, p.parseExpression(OpBitOr)} + precLeft = OpAnd + case OrToken: + if OpOr < prec { + return left + } else if precLeft < OpOr { + p.fail("expression") + return nil + } + p.next() + left = &BinaryExpr{tt, left, p.parseExpression(OpAnd)} + precLeft = OpOr + case NullishToken: + if OpCoalesce < prec { + return left + } else if precLeft < OpBitOr && precLeft != OpCoalesce { + p.fail("expression") + return nil + } + p.next() + left = &BinaryExpr{tt, left, p.parseExpression(OpBitOr)} + precLeft = OpCoalesce + case DotToken: + // OpMember < prec does never happen + if precLeft < OpCall { + p.fail("expression") + return nil + } + p.next() + if !IsIdentifierName(p.tt) && p.tt != PrivateIdentifierToken { + p.fail("dot expression", IdentifierToken) + return nil + } + exprPrec := OpMember + if precLeft < OpMember { + exprPrec = OpCall + } + if p.tt != PrivateIdentifierToken { + p.tt = IdentifierToken + } + left = &DotExpr{left, LiteralExpr{p.tt, p.data}, exprPrec, false} + p.next() + if precLeft < OpMember { + precLeft = OpCall + } else { + precLeft = OpMember + } + case OpenBracketToken: + // OpMember < prec does never happen + if precLeft < OpCall { + p.fail("expression") + return nil + } + p.next() + exprPrec := OpMember + if precLeft < OpMember { + exprPrec = OpCall + } + parentInFor := p.inFor + p.inFor = false + left = &IndexExpr{left, p.parseExpression(OpExpr), exprPrec, false} + p.inFor = parentInFor + if !p.consume("index expression", CloseBracketToken) { + return nil + } + if precLeft < OpMember { + precLeft = OpCall + } else { + precLeft = OpMember + } + case OpenParenToken: + if OpCall < prec { + return left + } else if precLeft < OpCall { + p.fail("expression") + return nil + } + parentInFor := p.inFor + p.inFor = false + left = &CallExpr{left, p.parseArguments(), false} + precLeft = OpCall + p.inFor = parentInFor + case TemplateToken, TemplateStartToken: + // OpMember < prec does never happen + if precLeft < OpCall { + p.fail("expression") + return nil + } + parentInFor := p.inFor + p.inFor = false + template := p.parseTemplateLiteral(precLeft) + template.Tag = left + left = &template + if precLeft < OpMember { + precLeft = OpCall + } else { + precLeft = OpMember + } + p.inFor = parentInFor + case OptChainToken: + if OpCall < prec { + return left + } + p.next() + if p.tt == OpenParenToken { + left = &CallExpr{left, p.parseArguments(), true} + } else if p.tt == OpenBracketToken { + p.next() + left = &IndexExpr{left, p.parseExpression(OpExpr), OpCall, true} + if !p.consume("optional chaining expression", CloseBracketToken) { + return nil + } + } else if p.tt == TemplateToken || p.tt == TemplateStartToken { + template := p.parseTemplateLiteral(precLeft) + template.Prec = OpCall + template.Tag = left + template.Optional = true + left = &template + } else if IsIdentifierName(p.tt) { + left = &DotExpr{left, LiteralExpr{IdentifierToken, p.data}, OpCall, true} + p.next() + } else if p.tt == PrivateIdentifierToken { + left = &DotExpr{left, LiteralExpr{p.tt, p.data}, OpCall, true} + p.next() + } else { + p.fail("optional chaining expression", IdentifierToken, OpenParenToken, OpenBracketToken, TemplateToken) + return nil + } + precLeft = OpCall + case IncrToken: + if p.prevLT || OpUpdate < prec { + return left + } else if precLeft < OpLHS { + p.fail("expression") + return nil + } + p.next() + left = &UnaryExpr{PostIncrToken, left} + precLeft = OpUpdate + case DecrToken: + if p.prevLT || OpUpdate < prec { + return left + } else if precLeft < OpLHS { + p.fail("expression") + return nil + } + p.next() + left = &UnaryExpr{PostDecrToken, left} + precLeft = OpUpdate + case ExpToken: + if OpExp < prec { + return left + } else if precLeft < OpUpdate { + p.fail("expression") + return nil + } + p.next() + left = &BinaryExpr{tt, left, p.parseExpression(OpExp)} + precLeft = OpExp + case MulToken, DivToken, ModToken: + if OpMul < prec { + return left + } else if precLeft < OpMul { + p.fail("expression") + return nil + } + p.next() + left = &BinaryExpr{tt, left, p.parseExpression(OpExp)} + precLeft = OpMul + case AddToken, SubToken: + if OpAdd < prec { + return left + } else if precLeft < OpAdd { + p.fail("expression") + return nil + } + p.next() + left = &BinaryExpr{tt, left, p.parseExpression(OpMul)} + precLeft = OpAdd + case LtLtToken, GtGtToken, GtGtGtToken: + if OpShift < prec { + return left + } else if precLeft < OpShift { + p.fail("expression") + return nil + } + p.next() + left = &BinaryExpr{tt, left, p.parseExpression(OpAdd)} + precLeft = OpShift + case BitAndToken: + if OpBitAnd < prec { + return left + } else if precLeft < OpBitAnd { + p.fail("expression") + return nil + } + p.next() + left = &BinaryExpr{tt, left, p.parseExpression(OpEquals)} + precLeft = OpBitAnd + case BitXorToken: + if OpBitXor < prec { + return left + } else if precLeft < OpBitXor { + p.fail("expression") + return nil + } + p.next() + left = &BinaryExpr{tt, left, p.parseExpression(OpBitAnd)} + precLeft = OpBitXor + case BitOrToken: + if OpBitOr < prec { + return left + } else if precLeft < OpBitOr { + p.fail("expression") + return nil + } + p.next() + left = &BinaryExpr{tt, left, p.parseExpression(OpBitXor)} + precLeft = OpBitOr + case QuestionToken: + if OpAssign < prec { + return left + } else if precLeft < OpCoalesce { + p.fail("expression") + return nil + } + p.next() + ifExpr := p.parseExpression(OpAssign) + if !p.consume("conditional expression", ColonToken) { + return nil + } + elseExpr := p.parseExpression(OpAssign) + left = &CondExpr{left, ifExpr, elseExpr} + precLeft = OpAssign + case CommaToken: + if OpExpr < prec { + return left + } + p.next() + if commaExpr, ok := left.(*CommaExpr); ok { + commaExpr.List = append(commaExpr.List, p.parseExpression(OpAssign)) + i-- // adjust expression nesting limit + } else { + left = &CommaExpr{[]IExpr{left, p.parseExpression(OpAssign)}} + } + precLeft = OpExpr + case ArrowToken: + // handle identifier => ..., where identifier could also be yield or await + if OpAssign < prec { + return left + } else if precLeft < OpPrimary { + p.fail("expression") + return nil + } + + v, ok := left.(*Var) + if !ok { + p.fail("expression") + return nil + } + + left = p.parseIdentifierArrowFunc(v) + precLeft = OpAssign + default: + return left + } + } +} + +func (p *Parser) parseAssignmentExpression() IExpr { + // this could be a BindingElement or an AssignmentExpression. Here we handle BindingIdentifier with a possible Initializer, BindingPattern will be handled by parseArrayLiteral or parseObjectLiteral + if p.assumeArrowFunc && p.isIdentifierReference(p.tt) { + tt := p.tt + data := p.data + p.next() + if p.tt == EqToken || p.tt == CommaToken || p.tt == CloseParenToken || p.tt == CloseBraceToken || p.tt == CloseBracketToken { + var ok bool + var left IExpr + left, ok = p.scope.Declare(ArgumentDecl, data) + if ok { + p.assumeArrowFunc = false + left = p.parseExpressionSuffix(left, OpAssign, OpPrimary) + p.assumeArrowFunc = true + return left + } + } + p.assumeArrowFunc = false + if tt == AsyncToken { + return p.parseAsyncExpression(OpAssign, data) + } + return p.parseIdentifierExpression(OpAssign, data) + } else if p.tt != OpenBracketToken && p.tt != OpenBraceToken { + p.assumeArrowFunc = false + } + return p.parseExpression(OpAssign) +} + +func (p *Parser) parseParenthesizedExpressionOrArrowFunc(prec OpPrec, async []byte) IExpr { + var left IExpr + precLeft := OpPrimary + + // expect to be at ( + p.next() + + isAsync := async != nil + arrowFunc := &ArrowFunc{} + parent := p.enterScope(&arrowFunc.Body.Scope, true) + parentAssumeArrowFunc, parentInFor := p.assumeArrowFunc, p.inFor + p.assumeArrowFunc, p.inFor = true, false + + // parse a parenthesized expression but assume we might be parsing an (async) arrow function. If this is really an arrow function, parsing as a parenthesized expression cannot fail as AssignmentExpression, ArrayLiteral, and ObjectLiteral are supersets of SingleNameBinding, ArrayBindingPattern, and ObjectBindingPattern respectively. Any identifier that would be a BindingIdentifier in case of an arrow function, will be added as such. If finally this is not an arrow function, we will demote those variables an undeclared and merge them with the parent scope. + + var list []IExpr + var rest IExpr + for p.tt != CloseParenToken && p.tt != ErrorToken { + if p.tt == EllipsisToken && p.assumeArrowFunc { + p.next() + if isAsync { + rest = p.parseAssignmentExpression() + if p.tt == CommaToken { + p.next() + } + } else if p.isIdentifierReference(p.tt) { + var ok bool + rest, ok = p.scope.Declare(ArgumentDecl, p.data) + if !ok { + p.failMessage("identifier %s has already been declared", string(p.data)) + return nil + } + p.next() + } else if p.tt == OpenBracketToken { + array := p.parseArrayLiteral() + rest = &array + } else if p.tt == OpenBraceToken { + object := p.parseObjectLiteral() + rest = &object + } else { + p.fail("arrow function") + return nil + } + break + } + + list = append(list, p.parseAssignmentExpression()) + if p.tt != CommaToken { + break + } + p.next() + } + if p.tt != CloseParenToken { + p.fail("expression") + return nil + } + p.next() + isArrowFunc := p.tt == ArrowToken && p.assumeArrowFunc + p.assumeArrowFunc, p.inFor = parentAssumeArrowFunc, parentInFor + + if isArrowFunc { + parentAwait, parentYield := p.await, p.yield + p.await = isAsync + + // arrow function + arrowFunc.Params = Params{List: make([]BindingElement, len(list))} + for i, item := range list { + arrowFunc.Params.List[i] = p.exprToBindingElement(item) // can not fail when assumArrowFunc is set + } + arrowFunc.Async = isAsync + arrowFunc.Params.Rest = p.exprToBinding(rest) + arrowFunc.Body.List = p.parseArrowFuncBody() + + p.await, p.yield = parentAwait, parentYield + p.exitScope(parent) + + left = arrowFunc + precLeft = OpAssign + } else if len(list) == 0 || !isAsync && rest != nil || isAsync && OpCall < prec { + p.fail("arrow function", ArrowToken) + return nil + } else { + p.exitScope(parent) + + // for any nested FuncExpr/ArrowFunc scope, Parent will point to the temporary scope created in case this was an arrow function instead of a parenthesized expression. This is not a problem as Parent is only used for defining new variables, and we already parsed all the nested scopes so that Parent (not Func) are not relevant anymore. Anyways, the Parent will just point to an empty scope, whose Parent/Func will point to valid scopes. This should not be a big deal. + // Here we move all declared ArgumentDecls (in case of an arrow function) to its parent scope as undeclared variables (identifiers used in a parenthesized expression). + arrowFunc.Body.Scope.UndeclareScope() + + if isAsync { + // call expression + args := Args{} + for _, item := range list { + args.List = append(args.List, Arg{Value: item, Rest: false}) + } + if rest != nil { + args.List = append(args.List, Arg{Value: rest, Rest: true}) + } + left = p.scope.Use(async) + left = &CallExpr{left, args, false} + precLeft = OpCall + } else { + // parenthesized expression + if 1 < len(list) { + left = &GroupExpr{&CommaExpr{list}} + } else { + left = &GroupExpr{list[0]} + } + } + } + return p.parseExpressionSuffix(left, prec, precLeft) +} + +// exprToBinding converts a CoverParenthesizedExpressionAndArrowParameterList into FormalParameters +// Any unbound variables of the parameters (Initializer, ComputedPropertyName) are kept in the parent scope +func (p *Parser) exprToBinding(expr IExpr) (binding IBinding) { + if v, ok := expr.(*Var); ok { + binding = v + } else if array, ok := expr.(*ArrayExpr); ok { + bindingArray := BindingArray{} + for _, item := range array.List { + if item.Spread { + // can only BindingIdentifier or BindingPattern + bindingArray.Rest = p.exprToBinding(item.Value) + break + } + var bindingElement BindingElement + bindingElement = p.exprToBindingElement(item.Value) + bindingArray.List = append(bindingArray.List, bindingElement) + } + binding = &bindingArray + } else if object, ok := expr.(*ObjectExpr); ok { + bindingObject := BindingObject{} + for _, item := range object.List { + if item.Spread { + // can only be BindingIdentifier + bindingObject.Rest = item.Value.(*Var) + break + } + var bindingElement BindingElement + bindingElement.Binding = p.exprToBinding(item.Value) + if bindingElement.Binding == nil { + bindingElement = p.exprToBindingElement(item.Value) + } else if item.Init != nil { + bindingElement.Default = item.Init + } + bindingObject.List = append(bindingObject.List, BindingObjectItem{Key: item.Name, Value: bindingElement}) + } + binding = &bindingObject + } + return +} + +func (p *Parser) exprToBindingElement(expr IExpr) (bindingElement BindingElement) { + if assign, ok := expr.(*BinaryExpr); ok && assign.Op == EqToken { + bindingElement.Binding = p.exprToBinding(assign.X) + bindingElement.Default = assign.Y + } else { + bindingElement.Binding = p.exprToBinding(expr) + } + return +} + +func (p *Parser) isIdentifierReference(tt TokenType) bool { + return IsIdentifier(tt) || tt == YieldToken && !p.yield || tt == AwaitToken && !p.await +} diff --git a/vendor/github.com/tdewolff/parse/v2/js/table.go b/vendor/github.com/tdewolff/parse/v2/js/table.go new file mode 100644 index 0000000..2533091 --- /dev/null +++ b/vendor/github.com/tdewolff/parse/v2/js/table.go @@ -0,0 +1,142 @@ +package js + +import "strconv" + +// OpPrec is the operator precedence +type OpPrec int + +// OpPrec values. +const ( + OpExpr OpPrec = iota // a,b + OpAssign // a?b:c, yield x, ()=>x, async ()=>x, a=b, a+=b, ... + OpCoalesce // a??b + OpOr // a||b + OpAnd // a&&b + OpBitOr // a|b + OpBitXor // a^b + OpBitAnd // a&b + OpEquals // a==b, a!=b, a===b, a!==b + OpCompare // a<b, a>b, a<=b, a>=b, a instanceof b, a in b + OpShift // a<<b, a>>b, a>>>b + OpAdd // a+b, a-b + OpMul // a*b, a/b, a%b + OpExp // a**b + OpUnary // ++x, --x, delete x, void x, typeof x, +x, -x, ~x, !x, await x + OpUpdate // x++, x-- + OpLHS // CallExpr/OptChainExpr or NewExpr + OpCall // a?.b, a(b), super(a), import(a) + OpNew // new a + OpMember // a[b], a.b, a`b`, super[x], super.x, new.target, import.meta, new a(b) + OpPrimary // literal, function, class, parenthesized +) + +func (prec OpPrec) String() string { + switch prec { + case OpExpr: + return "OpExpr" + case OpAssign: + return "OpAssign" + case OpCoalesce: + return "OpCoalesce" + case OpOr: + return "OpOr" + case OpAnd: + return "OpAnd" + case OpBitOr: + return "OpBitOr" + case OpBitXor: + return "OpBitXor" + case OpBitAnd: + return "OpBitAnd" + case OpEquals: + return "OpEquals" + case OpCompare: + return "OpCompare" + case OpShift: + return "OpShift" + case OpAdd: + return "OAdd" + case OpMul: + return "OpMul" + case OpExp: + return "OpExp" + case OpUnary: + return "OpUnary" + case OpUpdate: + return "OpUpdate" + case OpLHS: + return "OpLHS" + case OpCall: + return "OpCall" + case OpNew: + return "OpNew" + case OpMember: + return "OpMember" + case OpPrimary: + return "OpPrimary" + } + return "Invalid(" + strconv.Itoa(int(prec)) + ")" +} + +// Keywords is a map of reserved, strict, and other keywords +var Keywords = map[string]TokenType{ + // reserved + "await": AwaitToken, + "break": BreakToken, + "case": CaseToken, + "catch": CatchToken, + "class": ClassToken, + "const": ConstToken, + "continue": ContinueToken, + "debugger": DebuggerToken, + "default": DefaultToken, + "delete": DeleteToken, + "do": DoToken, + "else": ElseToken, + "enum": EnumToken, + "export": ExportToken, + "extends": ExtendsToken, + "false": FalseToken, + "finally": FinallyToken, + "for": ForToken, + "function": FunctionToken, + "if": IfToken, + "import": ImportToken, + "in": InToken, + "instanceof": InstanceofToken, + "new": NewToken, + "null": NullToken, + "return": ReturnToken, + "super": SuperToken, + "switch": SwitchToken, + "this": ThisToken, + "throw": ThrowToken, + "true": TrueToken, + "try": TryToken, + "typeof": TypeofToken, + "var": VarToken, + "void": VoidToken, + "while": WhileToken, + "with": WithToken, + "yield": YieldToken, + + // strict mode + "let": LetToken, + "static": StaticToken, + "implements": ImplementsToken, + "interface": InterfaceToken, + "package": PackageToken, + "private": PrivateToken, + "protected": ProtectedToken, + "public": PublicToken, + + // extra + "as": AsToken, + "async": AsyncToken, + "from": FromToken, + "get": GetToken, + "meta": MetaToken, + "of": OfToken, + "set": SetToken, + "target": TargetToken, +} diff --git a/vendor/github.com/tdewolff/parse/v2/js/tokentype.go b/vendor/github.com/tdewolff/parse/v2/js/tokentype.go new file mode 100644 index 0000000..4f310be --- /dev/null +++ b/vendor/github.com/tdewolff/parse/v2/js/tokentype.go @@ -0,0 +1,404 @@ +package js + +import "strconv" + +// TokenType determines the type of token, eg. a number or a semicolon. +type TokenType uint16 // from LSB to MSB: 8 bits for tokens per category, 1 bit for numeric, 1 bit for punctuator, 1 bit for operator, 1 bit for identifier, 4 bits unused + +// TokenType values. +const ( + ErrorToken TokenType = iota // extra token when errors occur + WhitespaceToken + LineTerminatorToken // \r \n \r\n + CommentToken + CommentLineTerminatorToken + StringToken + TemplateToken + TemplateStartToken + TemplateMiddleToken + TemplateEndToken + RegExpToken + PrivateIdentifierToken +) + +// Numeric token values. +const ( + NumericToken TokenType = 0x0100 + iota + DecimalToken + BinaryToken + OctalToken + HexadecimalToken + BigIntToken +) + +// Punctuator token values. +const ( + PunctuatorToken TokenType = 0x0200 + iota + OpenBraceToken // { + CloseBraceToken // } + OpenParenToken // ( + CloseParenToken // ) + OpenBracketToken // [ + CloseBracketToken // ] + DotToken // . + SemicolonToken // ; + CommaToken // , + QuestionToken // ? + ColonToken // : + ArrowToken // => + EllipsisToken // ... +) + +// Operator token values. +const ( + OperatorToken TokenType = 0x0600 + iota + EqToken // = + EqEqToken // == + EqEqEqToken // === + NotToken // ! + NotEqToken // != + NotEqEqToken // !== + LtToken // < + LtEqToken // <= + LtLtToken // << + LtLtEqToken // <<= + GtToken // > + GtEqToken // >= + GtGtToken // >> + GtGtEqToken // >>= + GtGtGtToken // >>> + GtGtGtEqToken // >>>= + AddToken // + + AddEqToken // += + IncrToken // ++ + SubToken // - + SubEqToken // -= + DecrToken // -- + MulToken // * + MulEqToken // *= + ExpToken // ** + ExpEqToken // **= + DivToken // / + DivEqToken // /= + ModToken // % + ModEqToken // %= + BitAndToken // & + BitOrToken // | + BitXorToken // ^ + BitNotToken // ~ + BitAndEqToken // &= + BitOrEqToken // |= + BitXorEqToken // ^= + AndToken // && + OrToken // || + NullishToken // ?? + AndEqToken // &&= + OrEqToken // ||= + NullishEqToken // ??= + OptChainToken // ?. + + // unused in lexer + PosToken // +a + NegToken // -a + PreIncrToken // ++a + PreDecrToken // --a + PostIncrToken // a++ + PostDecrToken // a-- +) + +// Reserved token values. +const ( + ReservedToken TokenType = 0x0800 + iota + AwaitToken + BreakToken + CaseToken + CatchToken + ClassToken + ConstToken + ContinueToken + DebuggerToken + DefaultToken + DeleteToken + DoToken + ElseToken + EnumToken + ExportToken + ExtendsToken + FalseToken + FinallyToken + ForToken + FunctionToken + IfToken + ImportToken + InToken + InstanceofToken + NewToken + NullToken + ReturnToken + SuperToken + SwitchToken + ThisToken + ThrowToken + TrueToken + TryToken + TypeofToken + YieldToken + VarToken + VoidToken + WhileToken + WithToken +) + +// Identifier token values. +const ( + IdentifierToken TokenType = 0x1000 + iota + AsToken + AsyncToken + FromToken + GetToken + ImplementsToken + InterfaceToken + LetToken + MetaToken + OfToken + PackageToken + PrivateToken + ProtectedToken + PublicToken + SetToken + StaticToken + TargetToken +) + +// IsNumeric return true if token is numeric. +func IsNumeric(tt TokenType) bool { + return tt&0x0100 != 0 +} + +// IsPunctuator return true if token is a punctuator. +func IsPunctuator(tt TokenType) bool { + return tt&0x0200 != 0 +} + +// IsOperator return true if token is an operator. +func IsOperator(tt TokenType) bool { + return tt&0x0400 != 0 +} + +// IsIdentifierName matches IdentifierName, i.e. any identifier +func IsIdentifierName(tt TokenType) bool { + return tt&0x1800 != 0 +} + +// IsReservedWord matches ReservedWord +func IsReservedWord(tt TokenType) bool { + return tt&0x0800 != 0 +} + +// IsIdentifier matches Identifier, i.e. IdentifierName but not ReservedWord. Does not match yield or await. +func IsIdentifier(tt TokenType) bool { + return tt&0x1000 != 0 +} + +func (tt TokenType) String() string { + s := tt.Bytes() + if s == nil { + return "Invalid(" + strconv.Itoa(int(tt)) + ")" + } + return string(s) +} + +var operatorBytes = [][]byte{ + []byte("Operator"), + []byte("="), + []byte("=="), + []byte("==="), + []byte("!"), + []byte("!="), + []byte("!=="), + []byte("<"), + []byte("<="), + []byte("<<"), + []byte("<<="), + []byte(">"), + []byte(">="), + []byte(">>"), + []byte(">>="), + []byte(">>>"), + []byte(">>>="), + []byte("+"), + []byte("+="), + []byte("++"), + []byte("-"), + []byte("-="), + []byte("--"), + []byte("*"), + []byte("*="), + []byte("**"), + []byte("**="), + []byte("/"), + []byte("/="), + []byte("%"), + []byte("%="), + []byte("&"), + []byte("|"), + []byte("^"), + []byte("~"), + []byte("&="), + []byte("|="), + []byte("^="), + []byte("&&"), + []byte("||"), + []byte("??"), + []byte("&&="), + []byte("||="), + []byte("??="), + []byte("?."), + []byte("+"), + []byte("-"), + []byte("++"), + []byte("--"), + []byte("++"), + []byte("--"), +} + +var reservedWordBytes = [][]byte{ + []byte("Reserved"), + []byte("await"), + []byte("break"), + []byte("case"), + []byte("catch"), + []byte("class"), + []byte("const"), + []byte("continue"), + []byte("debugger"), + []byte("default"), + []byte("delete"), + []byte("do"), + []byte("else"), + []byte("enum"), + []byte("export"), + []byte("extends"), + []byte("false"), + []byte("finally"), + []byte("for"), + []byte("function"), + []byte("if"), + []byte("import"), + []byte("in"), + []byte("instanceof"), + []byte("new"), + []byte("null"), + []byte("return"), + []byte("super"), + []byte("switch"), + []byte("this"), + []byte("throw"), + []byte("true"), + []byte("try"), + []byte("typeof"), + []byte("yield"), + []byte("var"), + []byte("void"), + []byte("while"), + []byte("with"), +} + +var identifierBytes = [][]byte{ + []byte("Identifier"), + []byte("as"), + []byte("async"), + []byte("from"), + []byte("get"), + []byte("implements"), + []byte("interface"), + []byte("let"), + []byte("meta"), + []byte("of"), + []byte("package"), + []byte("private"), + []byte("protected"), + []byte("public"), + []byte("set"), + []byte("static"), + []byte("target"), +} + +// Bytes returns the string representation of a TokenType. +func (tt TokenType) Bytes() []byte { + if IsOperator(tt) && int(tt-OperatorToken) < len(operatorBytes) { + return operatorBytes[tt-OperatorToken] + } else if IsReservedWord(tt) && int(tt-ReservedToken) < len(reservedWordBytes) { + return reservedWordBytes[tt-ReservedToken] + } else if IsIdentifier(tt) && int(tt-IdentifierToken) < len(identifierBytes) { + return identifierBytes[tt-IdentifierToken] + } + + switch tt { + case ErrorToken: + return []byte("Error") + case WhitespaceToken: + return []byte("Whitespace") + case LineTerminatorToken: + return []byte("LineTerminator") + case CommentToken: + return []byte("Comment") + case CommentLineTerminatorToken: + return []byte("CommentLineTerminator") + case StringToken: + return []byte("String") + case TemplateToken: + return []byte("Template") + case TemplateStartToken: + return []byte("TemplateStart") + case TemplateMiddleToken: + return []byte("TemplateMiddle") + case TemplateEndToken: + return []byte("TemplateEnd") + case RegExpToken: + return []byte("RegExp") + case PrivateIdentifierToken: + return []byte("PrivateIdentifier") + case NumericToken: + return []byte("Numeric") + case DecimalToken: + return []byte("Decimal") + case BinaryToken: + return []byte("Binary") + case OctalToken: + return []byte("Octal") + case HexadecimalToken: + return []byte("Hexadecimal") + case BigIntToken: + return []byte("BigInt") + case PunctuatorToken: + return []byte("Punctuator") + case OpenBraceToken: + return []byte("{") + case CloseBraceToken: + return []byte("}") + case OpenParenToken: + return []byte("(") + case CloseParenToken: + return []byte(")") + case OpenBracketToken: + return []byte("[") + case CloseBracketToken: + return []byte("]") + case DotToken: + return []byte(".") + case SemicolonToken: + return []byte(";") + case CommaToken: + return []byte(",") + case QuestionToken: + return []byte("?") + case ColonToken: + return []byte(":") + case ArrowToken: + return []byte("=>") + case EllipsisToken: + return []byte("...") + } + return nil +} diff --git a/vendor/github.com/tdewolff/parse/v2/js/util.go b/vendor/github.com/tdewolff/parse/v2/js/util.go new file mode 100644 index 0000000..78a629c --- /dev/null +++ b/vendor/github.com/tdewolff/parse/v2/js/util.go @@ -0,0 +1,38 @@ +package js + +// AsIdentifierName returns true if a valid identifier name is given. +func AsIdentifierName(b []byte) bool { + if len(b) == 0 || !identifierStartTable[b[0]] { + return false + } + + i := 1 + for i < len(b) { + if identifierTable[b[i]] { + i++ + } else { + return false + } + } + return true +} + +// AsDecimalLiteral returns true if a valid decimal literal is given. +func AsDecimalLiteral(b []byte) bool { + if len(b) == 0 || (b[0] < '0' || '9' < b[0]) && (b[0] != '.' || len(b) == 1) { + return false + } else if b[0] == '0' { + return len(b) == 1 + } + i := 1 + for i < len(b) && '0' <= b[i] && b[i] <= '9' { + i++ + } + if i < len(b) && b[i] == '.' && b[0] != '.' { + i++ + for i < len(b) && '0' <= b[i] && b[i] <= '9' { + i++ + } + } + return i == len(b) +} diff --git a/vendor/github.com/tdewolff/parse/v2/js/walk.go b/vendor/github.com/tdewolff/parse/v2/js/walk.go new file mode 100644 index 0000000..5c94473 --- /dev/null +++ b/vendor/github.com/tdewolff/parse/v2/js/walk.go @@ -0,0 +1,288 @@ +package js + +// IVisitor represents the AST Visitor +// Each INode encountered by `Walk` is passed to `Enter`, children nodes will be ignored if the returned IVisitor is nil +// `Exit` is called upon the exit of a node +type IVisitor interface { + Enter(n INode) IVisitor + Exit(n INode) +} + +// Walk traverses an AST in depth-first order +func Walk(v IVisitor, n INode) { + if n == nil { + return + } + + if v = v.Enter(n); v == nil { + return + } + + defer v.Exit(n) + + switch n := n.(type) { + case *AST: + Walk(v, &n.BlockStmt) + case *Var: + return + case *BlockStmt: + if n.List != nil { + for i := 0; i < len(n.List); i++ { + Walk(v, n.List[i]) + } + } + case *EmptyStmt: + return + case *ExprStmt: + Walk(v, n.Value) + case *IfStmt: + Walk(v, n.Body) + Walk(v, n.Else) + Walk(v, n.Cond) + case *DoWhileStmt: + Walk(v, n.Body) + Walk(v, n.Cond) + case *WhileStmt: + Walk(v, n.Body) + Walk(v, n.Cond) + case *ForStmt: + if n.Body != nil { + Walk(v, n.Body) + } + + Walk(v, n.Init) + Walk(v, n.Cond) + Walk(v, n.Post) + case *ForInStmt: + if n.Body != nil { + Walk(v, n.Body) + } + + Walk(v, n.Init) + Walk(v, n.Value) + case *ForOfStmt: + if n.Body != nil { + Walk(v, n.Body) + } + + Walk(v, n.Init) + Walk(v, n.Value) + case *CaseClause: + if n.List != nil { + for i := 0; i < len(n.List); i++ { + Walk(v, n.List[i]) + } + } + + Walk(v, n.Cond) + case *SwitchStmt: + if n.List != nil { + for i := 0; i < len(n.List); i++ { + Walk(v, &n.List[i]) + } + } + + Walk(v, n.Init) + case *BranchStmt: + return + case *ReturnStmt: + Walk(v, n.Value) + case *WithStmt: + Walk(v, n.Body) + Walk(v, n.Cond) + case *LabelledStmt: + Walk(v, n.Value) + case *ThrowStmt: + Walk(v, n.Value) + case *TryStmt: + if n.Body != nil { + Walk(v, n.Body) + } + + if n.Catch != nil { + Walk(v, n.Catch) + } + + if n.Finally != nil { + Walk(v, n.Finally) + } + + Walk(v, n.Binding) + case *DebuggerStmt: + return + case *Alias: + return + case *ImportStmt: + if n.List != nil { + for i := 0; i < len(n.List); i++ { + Walk(v, &n.List[i]) + } + } + case *ExportStmt: + if n.List != nil { + for i := 0; i < len(n.List); i++ { + Walk(v, &n.List[i]) + } + } + + Walk(v, n.Decl) + case *DirectivePrologueStmt: + return + case *PropertyName: + Walk(v, &n.Literal) + Walk(v, n.Computed) + case *BindingArray: + if n.List != nil { + for i := 0; i < len(n.List); i++ { + Walk(v, &n.List[i]) + } + } + + Walk(v, n.Rest) + case *BindingObjectItem: + if n.Key != nil { + Walk(v, n.Key) + } + + Walk(v, &n.Value) + case *BindingObject: + if n.List != nil { + for i := 0; i < len(n.List); i++ { + Walk(v, &n.List[i]) + } + } + + if n.Rest != nil { + Walk(v, n.Rest) + } + case *BindingElement: + Walk(v, n.Binding) + Walk(v, n.Default) + case *VarDecl: + if n.List != nil { + for i := 0; i < len(n.List); i++ { + Walk(v, &n.List[i]) + } + } + case *Params: + if n.List != nil { + for i := 0; i < len(n.List); i++ { + Walk(v, &n.List[i]) + } + } + + Walk(v, n.Rest) + case *FuncDecl: + Walk(v, &n.Body) + Walk(v, &n.Params) + + if n.Name != nil { + Walk(v, n.Name) + } + case *MethodDecl: + Walk(v, &n.Body) + Walk(v, &n.Params) + Walk(v, &n.Name) + case *Field: + Walk(v, &n.Name) + Walk(v, n.Init) + case *ClassDecl: + if n.Name != nil { + Walk(v, n.Name) + } + + Walk(v, n.Extends) + + for _, item := range n.List { + if item.StaticBlock != nil { + Walk(v, item.StaticBlock) + } else if item.Method != nil { + Walk(v, item.Method) + } else { + Walk(v, &item.Field) + } + } + case *LiteralExpr: + return + case *Element: + Walk(v, n.Value) + case *ArrayExpr: + if n.List != nil { + for i := 0; i < len(n.List); i++ { + Walk(v, &n.List[i]) + } + } + case *Property: + if n.Name != nil { + Walk(v, n.Name) + } + + Walk(v, n.Value) + Walk(v, n.Init) + case *ObjectExpr: + if n.List != nil { + for i := 0; i < len(n.List); i++ { + Walk(v, &n.List[i]) + } + } + case *TemplatePart: + Walk(v, n.Expr) + case *TemplateExpr: + if n.List != nil { + for i := 0; i < len(n.List); i++ { + Walk(v, &n.List[i]) + } + } + + Walk(v, n.Tag) + case *GroupExpr: + Walk(v, n.X) + case *IndexExpr: + Walk(v, n.X) + Walk(v, n.Y) + case *DotExpr: + Walk(v, n.X) + Walk(v, &n.Y) + case *NewTargetExpr: + return + case *ImportMetaExpr: + return + case *Arg: + Walk(v, n.Value) + case *Args: + if n.List != nil { + for i := 0; i < len(n.List); i++ { + Walk(v, &n.List[i]) + } + } + case *NewExpr: + if n.Args != nil { + Walk(v, n.Args) + } + + Walk(v, n.X) + case *CallExpr: + Walk(v, &n.Args) + Walk(v, n.X) + case *UnaryExpr: + Walk(v, n.X) + case *BinaryExpr: + Walk(v, n.X) + Walk(v, n.Y) + case *CondExpr: + Walk(v, n.Cond) + Walk(v, n.X) + Walk(v, n.Y) + case *YieldExpr: + Walk(v, n.X) + case *ArrowFunc: + Walk(v, &n.Body) + Walk(v, &n.Params) + case *CommaExpr: + for _, item := range n.List { + Walk(v, item) + } + default: + return + } +} |
