1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317 |
- package js
- import (
- "bytes"
- "fmt"
- "io"
- "strconv"
- "strings"
- "github.com/tdewolff/parse/v2"
- )
- var ErrInvalidJSON = fmt.Errorf("invalid JSON")
- type JSONer interface {
- JSON(io.Writer) error
- }
- // AST is the full ECMAScript abstract syntax tree.
- type AST struct {
- BlockStmt // module
- }
- func (ast AST) String() string {
- s := ""
- for i, item := range ast.BlockStmt.List {
- if i != 0 {
- s += " "
- }
- s += item.String()
- }
- return s
- }
- // JS writes JavaScript to writer.
- func (ast AST) JS(w io.Writer) {
- for i, item := range ast.List {
- if i != 0 {
- w.Write([]byte("\n"))
- }
- item.JS(w)
- if _, ok := item.(*VarDecl); ok {
- w.Write([]byte(";"))
- }
- }
- }
- // JSONString returns a string of JavaScript.
- func (ast AST) JSString() string {
- sb := strings.Builder{}
- ast.JS(&sb)
- return sb.String()
- }
- // JSON writes JSON to writer.
- func (ast AST) JSON(w io.Writer) error {
- if 1 < len(ast.List) {
- return ErrInvalidJSON
- } else if len(ast.List) == 0 {
- return nil
- } else if expr, ok := ast.List[0].(*ExprStmt); !ok {
- return ErrInvalidJSON
- } else if val, ok := expr.Value.(JSONer); !ok {
- return ErrInvalidJSON
- } else {
- return val.JSON(w)
- }
- return nil
- }
- // JSONString returns a string of JSON if valid.
- func (ast AST) JSONString() (string, error) {
- sb := strings.Builder{}
- err := ast.JSON(&sb)
- return sb.String(), err
- }
- ////////////////////////////////////////////////////////////////
- // 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) Info() string {
- s := fmt.Sprintf("%p type=%s name='%s' uses=%d", v, v.Decl, string(v.Data), v.Uses)
- links := 0
- for v.Link != nil {
- v = v.Link
- links++
- }
- if 0 < links {
- s += fmt.Sprintf(" links=%d => %p", links, v)
- }
- return s
- }
- func (v Var) String() string {
- return string(v.Name())
- }
- // JS writes JavaScript to writer.
- func (v Var) JS(w io.Writer) {
- w.Write(v.Name())
- }
- // VarsByUses is sortable by uses in descending order.
- type VarsByUses VarArray
- func (vs VarsByUses) Len() int {
- return len(vs)
- }
- func (vs VarsByUses) Swap(i, j int) {
- vs[i], vs[j] = vs[j], vs[i]
- }
- func (vs VarsByUses) Less(i, j int) bool {
- return vs[i].Uses > vs[j].Uses
- }
- ////////////////////////////////////////////////////////////////
- // VarArray is a set of variables in scopes.
- type VarArray []*Var
- func (vs VarArray) String() string {
- s := "["
- for i, v := range vs {
- if i != 0 {
- s += ", "
- }
- links := 0
- for v.Link != nil {
- v = v.Link
- links++
- }
- s += fmt.Sprintf("Var{%v %s %v %v}", v.Decl, string(v.Data), links, v.Uses)
- }
- return s + "]"
- }
- // Scope is a function or block scope with a list of variables declared and used.
- type Scope struct {
- Parent, Func *Scope // Parent is nil for global scope
- 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(io.Writer)
- }
- // 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()
- }
- ////////////////////////////////////////////////////////////////
- // Comment block or line, usually a bang comment.
- type Comment struct {
- Value []byte
- }
- func (n Comment) String() string {
- return "Stmt(" + string(n.Value) + ")"
- }
- // JS writes JavaScript to writer.
- func (n Comment) JS(w io.Writer) {
- if wi, ok := w.(Indenter); ok {
- w = wi.w
- }
- w.Write(n.Value)
- }
- // 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 writes JavaScript to writer.
- func (n BlockStmt) JS(w io.Writer) {
- if len(n.List) == 0 {
- w.Write([]byte("{}"))
- return
- }
- w.Write([]byte("{"))
- wi := NewIndenter(w, 4)
- for _, item := range n.List {
- wi.Write([]byte("\n"))
- item.JS(wi)
- if _, ok := item.(*VarDecl); ok {
- w.Write([]byte(";"))
- }
- }
- w.Write([]byte("\n}"))
- }
- // EmptyStmt is an empty statement.
- type EmptyStmt struct{}
- func (n EmptyStmt) String() string {
- return "Stmt()"
- }
- // JS writes JavaScript to writer.
- func (n EmptyStmt) JS(w io.Writer) {
- w.Write([]byte(";"))
- }
- // 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 writes JavaScript to writer.
- func (n ExprStmt) JS(w io.Writer) {
- n.Value.JS(w)
- w.Write([]byte(";"))
- }
- // 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 writes JavaScript to writer.
- func (n IfStmt) JS(w io.Writer) {
- w.Write([]byte("if ("))
- n.Cond.JS(w)
- w.Write([]byte(")"))
- if _, ok := n.Body.(*EmptyStmt); !ok {
- w.Write([]byte(" "))
- }
- n.Body.JS(w)
- if _, ok := n.Body.(*VarDecl); ok {
- w.Write([]byte(";"))
- }
- if n.Else != nil {
- w.Write([]byte(" else"))
- if _, ok := n.Else.(*EmptyStmt); !ok {
- w.Write([]byte(" "))
- }
- n.Else.JS(w)
- if _, ok := n.Else.(*VarDecl); ok {
- w.Write([]byte(";"))
- }
- }
- }
- // 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 writes JavaScript to writer.
- func (n DoWhileStmt) JS(w io.Writer) {
- w.Write([]byte("do"))
- if _, ok := n.Body.(*EmptyStmt); !ok {
- w.Write([]byte(" "))
- }
- n.Body.JS(w)
- if _, ok := n.Body.(*VarDecl); ok {
- w.Write([]byte(";"))
- }
- w.Write([]byte(" while ("))
- n.Cond.JS(w)
- w.Write([]byte(");"))
- }
- // 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 writes JavaScript to writer.
- func (n WhileStmt) JS(w io.Writer) {
- w.Write([]byte("while ("))
- n.Cond.JS(w)
- w.Write([]byte(")"))
- if _, ok := n.Body.(*EmptyStmt); !ok {
- w.Write([]byte(" "))
- }
- n.Body.JS(w)
- if _, ok := n.Body.(*VarDecl); ok {
- w.Write([]byte(";"))
- }
- }
- // 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 writes JavaScript to writer.
- func (n ForStmt) JS(w io.Writer) {
- w.Write([]byte("for ("))
- if v, ok := n.Init.(*VarDecl); !ok && n.Init != nil || ok && len(v.List) != 0 {
- n.Init.JS(w)
- } else {
- w.Write([]byte(" "))
- }
- w.Write([]byte("; "))
- if n.Cond != nil {
- n.Cond.JS(w)
- }
- w.Write([]byte("; "))
- if n.Post != nil {
- n.Post.JS(w)
- }
- w.Write([]byte(") "))
- n.Body.JS(w)
- }
- // 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 writes JavaScript to writer.
- func (n ForInStmt) JS(w io.Writer) {
- w.Write([]byte("for ("))
- n.Init.JS(w)
- w.Write([]byte(" in "))
- n.Value.JS(w)
- w.Write([]byte(") "))
- n.Body.JS(w)
- }
- // 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 writes JavaScript to writer.
- func (n ForOfStmt) JS(w io.Writer) {
- w.Write([]byte("for"))
- if n.Await {
- w.Write([]byte(" await"))
- }
- w.Write([]byte(" ("))
- n.Init.JS(w)
- w.Write([]byte(" of "))
- n.Value.JS(w)
- w.Write([]byte(") "))
- n.Body.JS(w)
- }
- // 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 writes JavaScript to writer.
- func (n CaseClause) JS(w io.Writer) {
- if n.Cond != nil {
- w.Write([]byte("case "))
- n.Cond.JS(w)
- } else {
- w.Write([]byte("default"))
- }
- w.Write([]byte(":"))
- wi := NewIndenter(w, 4)
- for _, item := range n.List {
- wi.Write([]byte("\n"))
- item.JS(wi)
- if _, ok := item.(*VarDecl); ok {
- w.Write([]byte(";"))
- }
- }
- }
- // 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 writes JavaScript to writer.
- func (n SwitchStmt) JS(w io.Writer) {
- w.Write([]byte("switch ("))
- n.Init.JS(w)
- if len(n.List) == 0 {
- w.Write([]byte(") {}"))
- return
- }
- w.Write([]byte(") {"))
- for _, clause := range n.List {
- w.Write([]byte("\n"))
- clause.JS(w)
- }
- w.Write([]byte("\n}"))
- }
- // 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 writes JavaScript to writer.
- func (n BranchStmt) JS(w io.Writer) {
- w.Write(n.Type.Bytes())
- if n.Label != nil {
- w.Write([]byte(" "))
- w.Write(n.Label)
- }
- w.Write([]byte(";"))
- }
- // 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 writes JavaScript to writer.
- func (n ReturnStmt) JS(w io.Writer) {
- w.Write([]byte("return"))
- if n.Value != nil {
- w.Write([]byte(" "))
- n.Value.JS(w)
- }
- w.Write([]byte(";"))
- }
- // 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 writes JavaScript to writer.
- func (n WithStmt) JS(w io.Writer) {
- w.Write([]byte("with ("))
- n.Cond.JS(w)
- w.Write([]byte(")"))
- if _, ok := n.Body.(*EmptyStmt); !ok {
- w.Write([]byte(" "))
- }
- n.Body.JS(w)
- if _, ok := n.Body.(*VarDecl); ok {
- w.Write([]byte(";"))
- }
- }
- // 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 writes JavaScript to writer.
- func (n LabelledStmt) JS(w io.Writer) {
- w.Write(n.Label)
- w.Write([]byte(":"))
- if _, ok := n.Value.(*EmptyStmt); !ok {
- w.Write([]byte(" "))
- }
- n.Value.JS(w)
- if _, ok := n.Value.(*VarDecl); ok {
- w.Write([]byte(";"))
- }
- }
- // ThrowStmt is a throw statement.
- type ThrowStmt struct {
- Value IExpr
- }
- func (n ThrowStmt) String() string {
- return "Stmt(throw " + n.Value.String() + ")"
- }
- // JS writes JavaScript to writer.
- func (n ThrowStmt) JS(w io.Writer) {
- w.Write([]byte("throw "))
- n.Value.JS(w)
- w.Write([]byte(";"))
- }
- // 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 writes JavaScript to writer.
- func (n TryStmt) JS(w io.Writer) {
- w.Write([]byte("try "))
- n.Body.JS(w)
- if n.Catch != nil {
- w.Write([]byte(" catch"))
- if n.Binding != nil {
- w.Write([]byte("("))
- n.Binding.JS(w)
- w.Write([]byte(")"))
- }
- w.Write([]byte(" "))
- n.Catch.JS(w)
- }
- if n.Finally != nil {
- w.Write([]byte(" finally "))
- n.Finally.JS(w)
- }
- }
- // DebuggerStmt is a debugger statement.
- type DebuggerStmt struct{}
- func (n DebuggerStmt) String() string {
- return "Stmt(debugger)"
- }
- // JS writes JavaScript to writer.
- func (n DebuggerStmt) JS(w io.Writer) {
- w.Write([]byte("debugger;"))
- }
- // Alias is a name space import or import/export specifier for import/export statements.
- type Alias struct {
- Name []byte // can be nil
- Binding []byte // can be nil
- }
- func (alias Alias) String() string {
- s := ""
- if alias.Name != nil {
- s += string(alias.Name) + " as "
- }
- return s + string(alias.Binding)
- }
- // JS writes JavaScript to writer.
- func (alias Alias) JS(w io.Writer) {
- if alias.Name != nil {
- w.Write(alias.Name)
- w.Write([]byte(" as "))
- }
- w.Write(alias.Binding)
- }
- // ImportStmt is an import statement.
- type ImportStmt struct {
- List []Alias
- Default []byte // can be nil
- Module []byte
- }
- func (n ImportStmt) String() string {
- s := "Stmt(import"
- if n.Default != nil {
- s += " " + string(n.Default)
- if n.List != nil {
- s += " ,"
- }
- }
- if len(n.List) == 1 && len(n.List[0].Name) == 1 && n.List[0].Name[0] == '*' {
- s += " " + n.List[0].String()
- } else if n.List != nil {
- s += " {"
- for i, item := range n.List {
- if i != 0 {
- s += " ,"
- }
- if item.Binding != nil {
- s += " " + item.String()
- }
- }
- s += " }"
- }
- if n.Default != nil || n.List != nil {
- s += " from"
- }
- return s + " " + string(n.Module) + ")"
- }
- // JS writes JavaScript to writer.
- func (n ImportStmt) JS(w io.Writer) {
- if wi, ok := w.(Indenter); ok {
- w = wi.w
- }
- w.Write([]byte("import"))
- if n.Default != nil {
- w.Write([]byte(" "))
- w.Write(n.Default)
- if n.List != nil {
- w.Write([]byte(","))
- }
- }
- if len(n.List) == 1 && len(n.List[0].Name) == 1 && n.List[0].Name[0] == '*' {
- w.Write([]byte(" "))
- n.List[0].JS(w)
- } else if n.List != nil {
- if len(n.List) == 0 {
- w.Write([]byte(" {}"))
- } else {
- w.Write([]byte(" {"))
- for j, item := range n.List {
- if j != 0 {
- w.Write([]byte(","))
- }
- if item.Binding != nil {
- w.Write([]byte(" "))
- item.JS(w)
- }
- }
- w.Write([]byte(" }"))
- }
- }
- if n.Default != nil || n.List != nil {
- w.Write([]byte(" from"))
- }
- w.Write([]byte(" "))
- w.Write(n.Module)
- w.Write([]byte(";"))
- }
- // 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 writes JavaScript to writer.
- func (n ExportStmt) JS(w io.Writer) {
- if wi, ok := w.(Indenter); ok {
- w = wi.w
- }
- w.Write([]byte("export"))
- if n.Decl != nil {
- if n.Default {
- w.Write([]byte(" default"))
- }
- w.Write([]byte(" "))
- n.Decl.JS(w)
- w.Write([]byte(";"))
- 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] == '*') {
- w.Write([]byte(" "))
- n.List[0].JS(w)
- } else if len(n.List) == 0 {
- w.Write([]byte(" {}"))
- } else {
- w.Write([]byte(" {"))
- for j, item := range n.List {
- if j != 0 {
- w.Write([]byte(","))
- }
- if item.Binding != nil {
- w.Write([]byte(" "))
- item.JS(w)
- }
- }
- w.Write([]byte(" }"))
- }
- if n.Module != nil {
- w.Write([]byte(" from "))
- w.Write(n.Module)
- }
- w.Write([]byte(";"))
- }
- // 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 writes JavaScript to writer.
- func (n DirectivePrologueStmt) JS(w io.Writer) {
- if wi, ok := w.(Indenter); ok {
- w = wi.w
- }
- w.Write(n.Value)
- w.Write([]byte(";"))
- }
- func (n Comment) stmtNode() {}
- 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 writes JavaScript to writer.
- func (n PropertyName) JS(w io.Writer) {
- if n.Computed != nil {
- w.Write([]byte("["))
- n.Computed.JS(w)
- w.Write([]byte("]"))
- return
- }
- if wi, ok := w.(Indenter); ok {
- w = wi.w
- }
- w.Write(n.Literal.Data)
- }
- // BindingArray is an array binding pattern.
- type BindingArray struct {
- List []BindingElement
- Rest IBinding // can be nil
- }
- func (n BindingArray) String() string {
- s := "["
- for i, item := range n.List {
- if i != 0 {
- s += ","
- }
- s += " " + item.String()
- }
- if n.Rest != nil {
- if len(n.List) != 0 {
- s += ","
- }
- s += " ...Binding(" + n.Rest.String() + ")"
- } else if 0 < len(n.List) && n.List[len(n.List)-1].Binding == nil {
- s += ","
- }
- return s + " ]"
- }
- // JS writes JavaScript to writer.
- func (n BindingArray) JS(w io.Writer) {
- w.Write([]byte("["))
- for j, item := range n.List {
- if j != 0 {
- w.Write([]byte(","))
- }
- if item.Binding != nil {
- if j != 0 {
- w.Write([]byte(" "))
- }
- item.JS(w)
- }
- }
- if n.Rest != nil {
- if len(n.List) != 0 {
- w.Write([]byte(", "))
- }
- w.Write([]byte("..."))
- n.Rest.JS(w)
- } else if 0 < len(n.List) && n.List[len(n.List)-1].Binding == nil {
- w.Write([]byte(","))
- }
- w.Write([]byte("]"))
- }
- // 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 writes JavaScript to writer.
- func (n BindingObjectItem) JS(w io.Writer) {
- if n.Key != nil {
- if v, ok := n.Value.Binding.(*Var); !ok || !n.Key.IsIdent(v.Data) {
- n.Key.JS(w)
- w.Write([]byte(": "))
- }
- }
- n.Value.JS(w)
- }
- // 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 writes JavaScript to writer.
- func (n BindingObject) JS(w io.Writer) {
- w.Write([]byte("{"))
- for j, item := range n.List {
- if j != 0 {
- w.Write([]byte(", "))
- }
- item.JS(w)
- }
- if n.Rest != nil {
- if len(n.List) != 0 {
- w.Write([]byte(", "))
- }
- w.Write([]byte("..."))
- w.Write(n.Rest.Data)
- }
- w.Write([]byte("}"))
- }
- // 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 writes JavaScript to writer.
- func (n BindingElement) JS(w io.Writer) {
- if n.Binding == nil {
- return
- }
- n.Binding.JS(w)
- if n.Default != nil {
- w.Write([]byte(" = "))
- n.Default.JS(w)
- }
- }
- 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 writes JavaScript to writer.
- func (n VarDecl) JS(w io.Writer) {
- w.Write(n.TokenType.Bytes())
- for j, item := range n.List {
- if j != 0 {
- w.Write([]byte(","))
- }
- w.Write([]byte(" "))
- item.JS(w)
- }
- }
- // 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 writes JavaScript to writer.
- func (n Params) JS(w io.Writer) {
- w.Write([]byte("("))
- for j, item := range n.List {
- if j != 0 {
- w.Write([]byte(", "))
- }
- item.JS(w)
- }
- if n.Rest != nil {
- if len(n.List) != 0 {
- w.Write([]byte(", "))
- }
- w.Write([]byte("..."))
- n.Rest.JS(w)
- }
- w.Write([]byte(")"))
- }
- // 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 writes JavaScript to writer.
- func (n FuncDecl) JS(w io.Writer) {
- if n.Async {
- w.Write([]byte("async function"))
- } else {
- w.Write([]byte("function"))
- }
- if n.Generator {
- w.Write([]byte("*"))
- }
- if n.Name != nil {
- w.Write([]byte(" "))
- w.Write(n.Name.Data)
- }
- n.Params.JS(w)
- w.Write([]byte(" "))
- n.Body.JS(w)
- }
- // 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 writes JavaScript to writer.
- func (n MethodDecl) JS(w io.Writer) {
- writen := false
- if n.Static {
- w.Write([]byte("static"))
- writen = true
- }
- if n.Async {
- if writen {
- w.Write([]byte(" "))
- }
- w.Write([]byte("async"))
- writen = true
- }
- if n.Generator {
- if writen {
- w.Write([]byte(" "))
- }
- w.Write([]byte("*"))
- writen = true
- }
- if n.Get {
- if writen {
- w.Write([]byte(" "))
- }
- w.Write([]byte("get"))
- writen = true
- }
- if n.Set {
- if writen {
- w.Write([]byte(" "))
- }
- w.Write([]byte("set"))
- writen = true
- }
- if writen {
- w.Write([]byte(" "))
- }
- n.Name.JS(w)
- w.Write([]byte(" "))
- n.Params.JS(w)
- w.Write([]byte(" "))
- n.Body.JS(w)
- }
- // 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 writes JavaScript to writer.
- func (n Field) JS(w io.Writer) {
- if n.Static {
- w.Write([]byte("static "))
- }
- n.Name.JS(w)
- if n.Init != nil {
- w.Write([]byte(" = "))
- n.Init.JS(w)
- }
- }
- // 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 writes JavaScript to writer.
- func (n ClassElement) JS(w io.Writer) {
- if n.StaticBlock != nil {
- w.Write([]byte("static "))
- n.StaticBlock.JS(w)
- return
- } else if n.Method != nil {
- n.Method.JS(w)
- return
- }
- n.Field.JS(w)
- w.Write([]byte(";"))
- }
- // 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 writes JavaScript to writer.
- func (n ClassDecl) JS(w io.Writer) {
- w.Write([]byte("class"))
- if n.Name != nil {
- w.Write([]byte(" "))
- w.Write(n.Name.Data)
- }
- if n.Extends != nil {
- w.Write([]byte(" extends "))
- n.Extends.JS(w)
- }
- if len(n.List) == 0 {
- w.Write([]byte(" {}"))
- return
- }
- w.Write([]byte(" {"))
- wi := NewIndenter(w, 4)
- for _, item := range n.List {
- wi.Write([]byte("\n"))
- item.JS(wi)
- }
- w.Write([]byte("\n}"))
- }
- 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 writes JavaScript to writer.
- func (n LiteralExpr) JS(w io.Writer) {
- if wi, ok := w.(Indenter); ok {
- w = wi.w
- }
- w.Write(n.Data)
- }
- // JSON writes JSON to writer.
- func (n LiteralExpr) JSON(w io.Writer) error {
- if n.TokenType == TrueToken || n.TokenType == FalseToken || n.TokenType == NullToken || n.TokenType == DecimalToken {
- w.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 = bytes.ReplaceAll(data, []byte(`"`), []byte(`\"`))
- data[0] = '"'
- data[len(data)-1] = '"'
- }
- w.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 writes JavaScript to writer.
- func (n Element) JS(w io.Writer) {
- if n.Value != nil {
- if n.Spread {
- w.Write([]byte("..."))
- }
- n.Value.JS(w)
- }
- }
- // 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 writes JavaScript to writer.
- func (n ArrayExpr) JS(w io.Writer) {
- w.Write([]byte("["))
- for j, item := range n.List {
- if j != 0 {
- w.Write([]byte(", "))
- }
- if item.Value != nil {
- if item.Spread {
- w.Write([]byte("..."))
- }
- item.Value.JS(w)
- }
- }
- if 0 < len(n.List) && n.List[len(n.List)-1].Value == nil {
- w.Write([]byte(","))
- }
- w.Write([]byte("]"))
- }
- // JSON writes JSON to writer.
- func (n ArrayExpr) JSON(w io.Writer) error {
- w.Write([]byte("["))
- for i, item := range n.List {
- if i != 0 {
- w.Write([]byte(", "))
- }
- if item.Value == nil || item.Spread {
- return ErrInvalidJSON
- }
- if val, ok := item.Value.(JSONer); !ok {
- return ErrInvalidJSON
- } else if err := val.JSON(w); err != nil {
- return err
- }
- }
- w.Write([]byte("]"))
- 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 writes JavaScript to writer.
- func (n Property) JS(w io.Writer) {
- if n.Name != nil {
- if v, ok := n.Value.(*Var); !ok || !n.Name.IsIdent(v.Data) {
- n.Name.JS(w)
- w.Write([]byte(": "))
- }
- } else if n.Spread {
- w.Write([]byte("..."))
- }
- n.Value.JS(w)
- if n.Init != nil {
- w.Write([]byte(" = "))
- n.Init.JS(w)
- }
- }
- // JSON writes JSON to writer.
- func (n Property) JSON(w io.Writer) 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 {
- w.Write([]byte(`"`))
- w.Write(n.Name.Literal.Data)
- w.Write([]byte(`"`))
- } else {
- _ = n.Name.Literal.JSON(w)
- }
- w.Write([]byte(": "))
- if val, ok := n.Value.(JSONer); !ok {
- return ErrInvalidJSON
- } else if err := val.JSON(w); 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 writes JavaScript to writer.
- func (n ObjectExpr) JS(w io.Writer) {
- w.Write([]byte("{"))
- for j, item := range n.List {
- if j != 0 {
- w.Write([]byte(", "))
- }
- item.JS(w)
- }
- w.Write([]byte("}"))
- }
- // JSON writes JSON to writer.
- func (n ObjectExpr) JSON(w io.Writer) error {
- w.Write([]byte("{"))
- for i, item := range n.List {
- if i != 0 {
- w.Write([]byte(", "))
- }
- if err := item.JSON(w); err != nil {
- return err
- }
- }
- w.Write([]byte("}"))
- 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 writes JavaScript to writer.
- func (n TemplatePart) JS(w io.Writer) {
- w.Write(n.Value)
- n.Expr.JS(w)
- }
- // 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 writes JavaScript to writer.
- func (n TemplateExpr) JS(w io.Writer) {
- if wi, ok := w.(Indenter); ok {
- w = wi.w
- }
- if n.Tag != nil {
- n.Tag.JS(w)
- if n.Optional {
- w.Write([]byte("?."))
- }
- }
- for _, item := range n.List {
- item.JS(w)
- }
- w.Write(n.Tail)
- }
- // GroupExpr is a parenthesized expression.
- type GroupExpr struct {
- X IExpr
- }
- func (n GroupExpr) String() string {
- return "(" + n.X.String() + ")"
- }
- // JS writes JavaScript to writer.
- func (n GroupExpr) JS(w io.Writer) {
- w.Write([]byte("("))
- n.X.JS(w)
- w.Write([]byte(")"))
- }
- // 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 writes JavaScript to writer.
- func (n IndexExpr) JS(w io.Writer) {
- n.X.JS(w)
- if n.Optional {
- w.Write([]byte("?.["))
- } else {
- w.Write([]byte("["))
- }
- n.Y.JS(w)
- w.Write([]byte("]"))
- }
- // 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 writes JavaScript to writer.
- func (n DotExpr) JS(w io.Writer) {
- lit, ok := n.X.(*LiteralExpr)
- group := ok && !n.Optional && lit.TokenType == DecimalToken
- if group {
- w.Write([]byte("("))
- }
- n.X.JS(w)
- if n.Optional {
- w.Write([]byte("?."))
- } else {
- if group {
- w.Write([]byte(")"))
- }
- w.Write([]byte("."))
- }
- n.Y.JS(w)
- }
- // NewTargetExpr is a new target meta property.
- type NewTargetExpr struct{}
- func (n NewTargetExpr) String() string {
- return "(new.target)"
- }
- // JS writes JavaScript to writer.
- func (n NewTargetExpr) JS(w io.Writer) {
- w.Write([]byte("new.target"))
- }
- // ImportMetaExpr is a import meta meta property.
- type ImportMetaExpr struct{}
- func (n ImportMetaExpr) String() string {
- return "(import.meta)"
- }
- // JS writes JavaScript to writer.
- func (n ImportMetaExpr) JS(w io.Writer) {
- w.Write([]byte("import.meta"))
- }
- type Arg struct {
- Value IExpr
- Rest bool
- }
- func (n Arg) String() string {
- s := ""
- if n.Rest {
- s += "..."
- }
- return s + n.Value.String()
- }
- // JS writes JavaScript to writer.
- func (n Arg) JS(w io.Writer) {
- if n.Rest {
- w.Write([]byte("..."))
- }
- n.Value.JS(w)
- }
- // 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 writes JavaScript to writer.
- func (n Args) JS(w io.Writer) {
- for j, item := range n.List {
- if j != 0 {
- w.Write([]byte(", "))
- }
- item.JS(w)
- }
- }
- // 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 writes JavaScript to writer.
- func (n NewExpr) JS(w io.Writer) {
- w.Write([]byte("new "))
- n.X.JS(w)
- if n.Args != nil {
- w.Write([]byte("("))
- n.Args.JS(w)
- w.Write([]byte(")"))
- } else {
- w.Write([]byte("()"))
- }
- }
- // 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 writes JavaScript to writer.
- func (n CallExpr) JS(w io.Writer) {
- n.X.JS(w)
- if n.Optional {
- w.Write([]byte("?.("))
- } else {
- w.Write([]byte("("))
- }
- n.Args.JS(w)
- w.Write([]byte(")"))
- }
- // 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 writes JavaScript to writer.
- func (n UnaryExpr) JS(w io.Writer) {
- if n.Op == PostIncrToken || n.Op == PostDecrToken {
- n.X.JS(w)
- w.Write(n.Op.Bytes())
- return
- } else if unary, ok := n.X.(*UnaryExpr); ok && (n.Op == PosToken && (unary.Op == PreIncrToken || unary.Op == PosToken) || n.Op == NegToken && (unary.Op == PreDecrToken || unary.Op == NegToken)) || IsIdentifierName(n.Op) {
- w.Write(n.Op.Bytes())
- w.Write([]byte(" "))
- n.X.JS(w)
- return
- }
- w.Write(n.Op.Bytes())
- n.X.JS(w)
- }
- // JSON writes JSON to writer.
- func (n UnaryExpr) JSON(w io.Writer) error {
- if lit, ok := n.X.(*LiteralExpr); ok && n.Op == NegToken && lit.TokenType == DecimalToken {
- w.Write([]byte("-"))
- w.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 writes JavaScript to writer.
- func (n BinaryExpr) JS(w io.Writer) {
- n.X.JS(w)
- w.Write([]byte(" "))
- w.Write(n.Op.Bytes())
- w.Write([]byte(" "))
- n.Y.JS(w)
- }
- // 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 writes JavaScript to writer.
- func (n CondExpr) JS(w io.Writer) {
- n.Cond.JS(w)
- w.Write([]byte(" ? "))
- n.X.JS(w)
- w.Write([]byte(" : "))
- n.Y.JS(w)
- }
- // 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 writes JavaScript to writer.
- func (n YieldExpr) JS(w io.Writer) {
- w.Write([]byte("yield"))
- if n.X == nil {
- return
- }
- if n.Generator {
- w.Write([]byte("*"))
- }
- w.Write([]byte(" "))
- n.X.JS(w)
- }
- // 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 writes JavaScript to writer.
- func (n ArrowFunc) JS(w io.Writer) {
- if n.Async {
- w.Write([]byte("async "))
- }
- n.Params.JS(w)
- w.Write([]byte(" => "))
- n.Body.JS(w)
- }
- // 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 writes JavaScript to writer.
- func (n CommaExpr) JS(w io.Writer) {
- for j, item := range n.List {
- if j != 0 {
- w.Write([]byte(","))
- }
- item.JS(w)
- }
- }
- 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() {}
|