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() {}