12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288 |
- 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) {
- w.Write(n.Value)
- w.Write([]byte("\n"))
- }
- // 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) {
- 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) {
- 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) {
- 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
- }
- 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() + ")"
- }
- 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(", "))
- }
- 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("]"))
- }
- // 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) {
- 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 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() {}
|