From 69a1e4327c899f1c85a5d89d0da3829c7ed4c86c Mon Sep 17 00:00:00 2001 From: haya14busa Date: Tue, 20 Sep 2016 02:18:58 +0900 Subject: [PATCH 1/8] prof.0: initial profiling. NewExportNode seems to have a room for improvement $ go test -v -run="^$" -bench="." -benchtime=5s -cpuprofile=prof.cpu -memprofile=prof.mem | tee prof.0 BenchmarkParseFile-4 30 245095530 ns/op $ go tool pprof --alloc_space go-vimlparser.test prof.mem Entering interactive mode (type "help" for commands) (pprof) top 1401.64MB of 1678.59MB total (83.50%) Dropped 27 nodes (cum <= 8.39MB) Showing top 10 nodes out of 56 (cum >= 143.54MB) flat flat% sum% cum cum% 297.09MB 17.70% 17.70% 345.10MB 20.56% github.com/haya14busa/go-vimlparser/go.NewExportNode 254.71MB 15.17% 32.87% 254.71MB 15.17% github.com/haya14busa/go-vimlparser/go.(*StringReader).__init__ 195.13MB 11.62% 44.50% 352.14MB 20.98% github.com/haya14busa/go-vimlparser/go.(*ExprTokenizer).get 193.57MB 11.53% 56.03% 468.70MB 27.92% github.com/haya14busa/go-vimlparser/go.(*ExprParser).parse_expr9 99.08MB 5.90% 61.93% 166.59MB 9.92% github.com/haya14busa/go-vimlparser/internal/exporter.NewNode 83MB 4.94% 66.88% 83MB 4.94% reflect.unsafe_New 75.52MB 4.50% 71.38% 78.02MB 4.65% github.com/haya14busa/go-vimlparser/go.(*ExprParser).parse_curly_parts 71.52MB 4.26% 75.64% 77.02MB 4.59% github.com/haya14busa/go-vimlparser/go.(*ExprParser).parse_dot 66.50MB 3.96% 79.60% 74MB 4.41% github.com/haya14busa/go-vimlparser/go.(*ExprTokenizer).get2 65.52MB 3.90% 83.50% 143.54MB 8.55% github.com/haya14busa/go-vimlparser/go.(*ExprParser).parse_identifier (pprof) list NewExportNode Total: 1.64GB ROUTINE ======================== github.com/haya14busa/go-vimlparser/go.NewExportNode in /home/haya14busa/src/github.com/haya14busa/go-vimlparser/go/export.go 297.09MB 1.10GB (flat, cum) 66.83% of Total . . 132: if n == nil { . . 133: return nil . . 134: } . . 135: list := make([]*ExportNode, 0) . . 136: for _, n := range n.list { . 1.50MB 137: list = append(list, NewExportNode(n)) . . 138: } . . 139: rlist := make([]*ExportNode, 0) . . 140: for _, n := range n.rlist { . 34.51MB 141: rlist = append(rlist, NewExportNode(n)) . . 142: } . . 143: body := make([]*ExportNode, 0) . . 144: for _, n := range n.body { 2.01MB 294.08MB 145: body = append(body, NewExportNode(n)) . . 146: } . . 147: elseif := make([]*ExportNode, 0) . . 148: for _, n := range n.elseif { . 114.53MB 149: elseif = append(elseif, NewExportNode(n)) . . 150: } . . 151: catch := make([]*ExportNode, 0) . . 152: for _, n := range n.catch { . . 153: catch = append(catch, NewExportNode(n)) . . 154: } . . 155: return &ExportNode{ . . 156: Type: n.type_, 18MB 18MB 157: Pos: NewExportPos(n.pos), . 150.54MB 158: Left: NewExportNode(n.left), . 86.53MB 159: Right: NewExportNode(n.right), . 54.02MB 160: Cond: NewExportNode(n.cond), . . 161: Rest: NewExportNode(n.rest), . . 162: List: list, . . 163: Rlist: rlist, . . 164: Body: body, . . 165: Op: n.op, . . 166: Str: n.str, . . 167: Depth: n.depth, . . 168: Value: n.value, . . 169: . 48.01MB 170: Ea: NewExportExArg(n.ea), 6MB 6MB 171: Attr: NewExportFuncAttr(n.attr), . . 172: . 8MB 173: Endfunction: NewExportNode(n.endfunction), . . 174: Elseif: elseif, . 28.01MB 175: Else: NewExportNode(n.else_), . 6.50MB 176: Endif: NewExportNode(n.endif), . 512.16kB 177: Endwhile: NewExportNode(n.endwhile), . . 178: Endfor: NewExportNode(n.endfor), . . 179: Endtry: NewExportNode(n.endtry), . . 180: . . 181: Catch: catch, . . 182: Finally: NewExportNode(n.finally), . . 183: . . 184: Pattern: n.pattern, 271.08MB 271.08MB 185: Curly: n.curly, . . 186: } . . 187:} . . 188: . . 189:type ExportFuncAttr struct { . . 190: Range bool $ go tool pprof go-vimlparser.test prof.cpu Entering interactive mode (type "help" for commands) (pprof) top 4020ms of 9040ms total (44.47%) Dropped 108 nodes (cum <= 45.20ms) Showing top 10 nodes out of 151 (cum >= 420ms) flat flat% sum% cum cum% 1020ms 11.28% 11.28% 1710ms 18.92% runtime.scanobject 750ms 8.30% 19.58% 1430ms 15.82% runtime.mallocgc 330ms 3.65% 23.23% 760ms 8.41% regexp.(*machine).onepass 320ms 3.54% 26.77% 320ms 3.54% sync/atomic.CompareAndSwapUint32 290ms 3.21% 29.98% 290ms 3.21% runtime.heapBitsForObject 290ms 3.21% 33.19% 430ms 4.76% runtime.mapaccess2_faststr 270ms 2.99% 36.17% 270ms 2.99% sync/atomic.AddUint32 260ms 2.88% 39.05% 620ms 6.86% github.com/haya14busa/go-vimlparser/go.NewExportNode 260ms 2.88% 41.92% 540ms 5.97% sync.(*Mutex).Unlock 230ms 2.54% 44.47% 420ms 4.65% runtime.greyobject From 1bae2fd10293f83c3d8662dccb628ec84dce47d7 Mon Sep 17 00:00:00 2001 From: haya14busa Date: Tue, 20 Sep 2016 02:31:01 +0900 Subject: [PATCH 2/8] slice allocation $ benchcmp prof.0 prof.1 benchmark old ns/op new ns/op delta BenchmarkParseFile-4 245095530 244779103 -0.13% --- go/export.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/go/export.go b/go/export.go index 340ac92..2064026 100644 --- a/go/export.go +++ b/go/export.go @@ -132,23 +132,23 @@ func NewExportNode(n *VimNode) *ExportNode { if n == nil { return nil } - list := make([]*ExportNode, 0) + list := make([]*ExportNode, 0, len(n.list)) for _, n := range n.list { list = append(list, NewExportNode(n)) } - rlist := make([]*ExportNode, 0) + rlist := make([]*ExportNode, 0, len(n.rlist)) for _, n := range n.rlist { rlist = append(rlist, NewExportNode(n)) } - body := make([]*ExportNode, 0) + body := make([]*ExportNode, 0, len(n.body)) for _, n := range n.body { body = append(body, NewExportNode(n)) } - elseif := make([]*ExportNode, 0) + elseif := make([]*ExportNode, 0, len(n.elseif)) for _, n := range n.elseif { elseif = append(elseif, NewExportNode(n)) } - catch := make([]*ExportNode, 0) + catch := make([]*ExportNode, 0, len(n.catch)) for _, n := range n.catch { catch = append(catch, NewExportNode(n)) } @@ -228,23 +228,23 @@ func newInternalNode(n *ExportNode) *VimNode { if n == nil { return nil } - list := make([]*VimNode, 0) + list := make([]*VimNode, 0, len(n.List)) for _, n := range n.List { list = append(list, newInternalNode(n)) } - rlist := make([]*VimNode, 0) + rlist := make([]*VimNode, 0, len(n.Rlist)) for _, n := range n.Rlist { rlist = append(rlist, newInternalNode(n)) } - body := make([]*VimNode, 0) + body := make([]*VimNode, 0, len(n.Body)) for _, n := range n.Body { body = append(body, newInternalNode(n)) } - elseif := make([]*VimNode, 0) + elseif := make([]*VimNode, 0, len(n.Elseif)) for _, n := range n.Elseif { elseif = append(elseif, newInternalNode(n)) } - catch := make([]*VimNode, 0) + catch := make([]*VimNode, 0, len(n.Catch)) for _, n := range n.Catch { catch = append(catch, newInternalNode(n)) } From d41aaad323c07856d0544820c07a3a6035316c4b Mon Sep 17 00:00:00 2001 From: haya14busa Date: Tue, 20 Sep 2016 03:11:33 +0900 Subject: [PATCH 3/8] move exporter pkg func to internal and reduce unneeded conversions $ benchcmp prof.1 prof.2 benchmark old ns/op new ns/op delta BenchmarkParseFile-4 244779103 225677916 -7.80% $ go tool pprof --alloc_space go-vimlparser.test prof.mem Entering interactive mode (type "help" for commands) (pprof) top 1153.55MB of 1363.78MB total (84.58%) Dropped 30 nodes (cum <= 6.82MB) Showing top 10 nodes out of 56 (cum >= 814.41MB) flat flat% sum% cum cum% 261.21MB 19.15% 19.15% 261.21MB 19.15% github.com/haya14busa/go-vimlparser/go.(*StringReader).__init__ 194.56MB 14.27% 33.42% 480.71MB 35.25% github.com/haya14busa/go-vimlparser/go.(*ExprParser).parse_expr9 185.66MB 13.61% 47.03% 348.66MB 25.57% github.com/haya14busa/go-vimlparser/go.(*ExprTokenizer).get 98.50MB 7.22% 54.26% 98.50MB 7.22% reflect.unsafe_New 88.56MB 6.49% 60.75% 112.06MB 8.22% github.com/haya14busa/go-vimlparser/go.NewNode 77.52MB 5.68% 66.43% 84.02MB 6.16% github.com/haya14busa/go-vimlparser/go.(*ExprParser).parse_curly_parts 70.52MB 5.17% 71.60% 154.54MB 11.33% github.com/haya14busa/go-vimlparser/go.(*ExprParser).parse_identifier 69.52MB 5.10% 76.70% 74.02MB 5.43% github.com/haya14busa/go-vimlparser/go.(*ExprParser).parse_dot 59MB 4.33% 81.03% 64.50MB 4.73% github.com/haya14busa/go-vimlparser/go.(*ExprTokenizer).get2 48.51MB 3.56% 84.58% 814.41MB 59.72% github.com/haya14busa/go-vimlparser/go.(*VimLParser).parse_one_cmd --- go/export.go | 880 +++++++++++++++++++++++++------------ internal/exporter/exarg.go | 45 -- internal/exporter/node.go | 501 --------------------- internal/exporter/pos.go | 15 - internal/exporter/token.go | 94 ---- vimlparser.go | 5 +- 6 files changed, 592 insertions(+), 948 deletions(-) delete mode 100644 internal/exporter/exarg.go delete mode 100644 internal/exporter/node.go delete mode 100644 internal/exporter/pos.go delete mode 100644 internal/exporter/token.go diff --git a/go/export.go b/go/export.go index 2064026..4827ec3 100644 --- a/go/export.go +++ b/go/export.go @@ -1,37 +1,465 @@ package vimlparser -type ExportExArg struct { - Forceit bool - AddrCount int - Line1 int - Line2 int - Flags int - DoEcmdCmd string - DoEcmdLnum int - Append int - Usefilter bool - Amount int - Regname int - ForceBin int - ReadEdit int - ForceFf string // int - ForceEnc string // int - BadChar string // int - Linepos *ExportPos - Cmdpos *ExportPos - Argpos *ExportPos - Cmd *ExportCmd - Modifiers []interface{} - Range []interface{} - Argopt map[string]interface{} - Argcmd map[string]interface{} +import ( + "fmt" + + "github.com/haya14busa/go-vimlparser/ast" + "github.com/haya14busa/go-vimlparser/token" +) + +func (self *VimLParser) Parse(reader *StringReader) ast.Node { + return NewNode(self.parse(reader)) +} + +func (self *ExprParser) Parse() ast.Node { + return NewNode(self.parse()) } -func NewExportExArg(ea *ExArg) *ExportExArg { - if ea == nil { +// ---- + +// NewNode converts internal node type to ast.Node. +// n.type_ must no be zero value. +// n.pos must no be nil except TOPLEVEL node. +func NewNode(n *VimNode) ast.Node { + if n == nil { return nil } - return &ExportExArg{ + + // TOPLEVEL doens't have position...? + var pos ast.Pos + if p := newPos(n.pos); p != nil { + pos = *p + } else { + pos = ast.Pos{Offset: 0, Line: 1, Column: 1} + } + + switch n.type_ { + + case NODE_TOPLEVEL: + return &ast.File{Start: pos, Body: newBody(*n)} + + case NODE_COMMENT: + return &ast.Comment{ + Quote: pos, + Text: n.str, + } + + case NODE_EXCMD: + return &ast.Excmd{ + Excmd: pos, + ExArg: newExArg(*n.ea), + Command: n.str, + } + + case NODE_FUNCTION: + attr := ast.FuncAttr{} + if n.attr != nil { + attr = ast.FuncAttr{ + Range: n.attr.range_, + Abort: n.attr.abort, + Dict: n.attr.dict, + Closure: n.attr.closure, + } + } + return &ast.Function{ + Func: pos, + ExArg: newExArg(*n.ea), + Body: newBody(*n), + Name: NewNode(n.left), + Params: newIdents(*n), + Attr: attr, + EndFunction: *NewNode(n.endfunction).(*ast.EndFunction), + } + + case NODE_ENDFUNCTION: + return &ast.EndFunction{ + EndFunc: pos, + ExArg: newExArg(*n.ea), + } + + case NODE_DELFUNCTION: + return &ast.DelFunction{ + DelFunc: pos, + ExArg: newExArg(*n.ea), + Name: NewNode(n.left), + } + + case NODE_RETURN: + return &ast.Return{ + Return: pos, + ExArg: newExArg(*n.ea), + Result: NewNode(n.left), + } + + case NODE_EXCALL: + return &ast.ExCall{ + ExCall: pos, + ExArg: newExArg(*n.ea), + FuncCall: *NewNode(n.left).(*ast.CallExpr), + } + + case NODE_LET: + return &ast.Let{ + Let: pos, + ExArg: newExArg(*n.ea), + Op: n.op, + Left: NewNode(n.left), + List: newList(*n), + Rest: NewNode(n.rest), + Right: NewNode(n.right), + } + + case NODE_UNLET: + return &ast.UnLet{ + UnLet: pos, + ExArg: newExArg(*n.ea), + List: newList(*n), + } + + case NODE_LOCKVAR: + return &ast.LockVar{ + LockVar: pos, + ExArg: newExArg(*n.ea), + Depth: n.depth, + List: newList(*n), + } + + case NODE_UNLOCKVAR: + return &ast.UnLockVar{ + UnLockVar: pos, + ExArg: newExArg(*n.ea), + Depth: n.depth, + List: newList(*n), + } + + case NODE_IF: + var elifs []ast.ElseIf + if n.elseif != nil { + elifs = make([]ast.ElseIf, 0, len(n.elseif)) + } + for _, node := range n.elseif { + if node != nil { // conservative + elifs = append(elifs, *NewNode(node).(*ast.ElseIf)) + } + } + var els *ast.Else + if n.else_ != nil { + els = NewNode(n.else_).(*ast.Else) + } + return &ast.If{ + If: pos, + ExArg: newExArg(*n.ea), + Body: newBody(*n), + Condition: NewNode(n.cond), + ElseIf: elifs, + Else: els, + EndIf: *NewNode(n.endif).(*ast.EndIf), + } + + case NODE_ELSEIF: + return &ast.ElseIf{ + ElseIf: pos, + ExArg: newExArg(*n.ea), + Body: newBody(*n), + Condition: NewNode(n.cond), + } + + case NODE_ELSE: + return &ast.Else{ + Else: pos, + ExArg: newExArg(*n.ea), + Body: newBody(*n), + } + + case NODE_ENDIF: + return &ast.EndIf{ + EndIf: pos, + ExArg: newExArg(*n.ea), + } + + case NODE_WHILE: + return &ast.While{ + While: pos, + ExArg: newExArg(*n.ea), + Body: newBody(*n), + Condition: NewNode(n.cond), + EndWhile: *NewNode(n.endwhile).(*ast.EndWhile), + } + + case NODE_ENDWHILE: + return &ast.EndWhile{ + EndWhile: pos, + ExArg: newExArg(*n.ea), + } + + case NODE_FOR: + return &ast.For{ + For: pos, + ExArg: newExArg(*n.ea), + Body: newBody(*n), + Left: NewNode(n.left), + List: newList(*n), + Rest: NewNode(n.rest), + Right: NewNode(n.right), + EndFor: *NewNode(n.endfor).(*ast.EndFor), + } + + case NODE_ENDFOR: + return &ast.EndFor{ + EndFor: pos, + ExArg: newExArg(*n.ea), + } + + case NODE_CONTINUE: + return &ast.Continue{ + Continue: pos, + ExArg: newExArg(*n.ea), + } + + case NODE_BREAK: + return &ast.Break{ + Break: pos, + ExArg: newExArg(*n.ea), + } + + case NODE_TRY: + var catches []ast.Catch + if n.catch != nil { + catches = make([]ast.Catch, 0, len(n.catch)) + } + for _, node := range n.catch { + if node != nil { // conservative + catches = append(catches, *NewNode(node).(*ast.Catch)) + } + } + var finally *ast.Finally + if n.finally != nil { + finally = NewNode(n.finally).(*ast.Finally) + } + return &ast.Try{ + Try: pos, + ExArg: newExArg(*n.ea), + Body: newBody(*n), + Catch: catches, + Finally: finally, + EndTry: *NewNode(n.endtry).(*ast.EndTry), + } + + case NODE_CATCH: + return &ast.Catch{ + Catch: pos, + ExArg: newExArg(*n.ea), + Body: newBody(*n), + Pattern: n.pattern, + } + + case NODE_FINALLY: + return &ast.Finally{ + Finally: pos, + ExArg: newExArg(*n.ea), + Body: newBody(*n), + } + + case NODE_ENDTRY: + return &ast.EndTry{ + EndTry: pos, + ExArg: newExArg(*n.ea), + } + + case NODE_THROW: + return &ast.Throw{ + Throw: pos, + ExArg: newExArg(*n.ea), + Expr: NewNode(n.left), + } + + case NODE_ECHO, NODE_ECHON, NODE_ECHOMSG, NODE_ECHOERR: + return &ast.EchoCmd{ + Start: pos, + CmdName: n.ea.cmd.name, + ExArg: newExArg(*n.ea), + Exprs: newList(*n), + } + + case NODE_ECHOHL: + return &ast.Echohl{ + Echohl: pos, + ExArg: newExArg(*n.ea), + Name: n.str, + } + + case NODE_EXECUTE: + return &ast.Execute{ + Execute: pos, + ExArg: newExArg(*n.ea), + Exprs: newList(*n), + } + + case NODE_TERNARY: + return &ast.TernaryExpr{ + Ternary: pos, + Condition: NewNode(n.cond), + Left: NewNode(n.left), + Right: NewNode(n.right), + } + + case NODE_OR, NODE_AND, NODE_EQUAL, NODE_EQUALCI, NODE_EQUALCS, + NODE_NEQUAL, NODE_NEQUALCI, NODE_NEQUALCS, NODE_GREATER, + NODE_GREATERCI, NODE_GREATERCS, NODE_GEQUAL, NODE_GEQUALCI, + NODE_GEQUALCS, NODE_SMALLER, NODE_SMALLERCI, NODE_SMALLERCS, + NODE_SEQUAL, NODE_SEQUALCI, NODE_SEQUALCS, NODE_MATCH, + NODE_MATCHCI, NODE_MATCHCS, NODE_NOMATCH, NODE_NOMATCHCI, + NODE_NOMATCHCS, NODE_IS, NODE_ISCI, NODE_ISCS, NODE_ISNOT, + NODE_ISNOTCI, NODE_ISNOTCS, NODE_ADD, NODE_SUBTRACT, NODE_CONCAT, + NODE_MULTIPLY, NODE_DIVIDE, NODE_REMAINDER: + return &ast.BinaryExpr{ + Left: NewNode(n.left), + OpPos: pos, + Op: opToken(n.type_), + Right: NewNode(n.right), + } + + case NODE_NOT, NODE_MINUS, NODE_PLUS: + return &ast.UnaryExpr{ + OpPos: pos, + Op: opToken(n.type_), + X: NewNode(n.left), + } + + case NODE_SUBSCRIPT: + return &ast.SubscriptExpr{ + Lbrack: pos, + Left: NewNode(n.left), + Right: NewNode(n.right), + } + + case NODE_SLICE: + return &ast.SliceExpr{ + Lbrack: pos, + X: NewNode(n.left), + Low: NewNode(n.rlist[0]), + High: NewNode(n.rlist[1]), + } + + case NODE_CALL: + return &ast.CallExpr{ + Lparen: pos, + Fun: NewNode(n.left), + Args: newRlist(*n), + } + + case NODE_DOT: + return &ast.DotExpr{ + Left: NewNode(n.left), + Dot: pos, + Right: *NewNode(n.right).(*ast.Ident), + } + + case NODE_NUMBER: + return &ast.BasicLit{ + ValuePos: pos, + Kind: token.NUMBER, + Value: n.value.(string), + } + case NODE_STRING: + return &ast.BasicLit{ + ValuePos: pos, + Kind: token.STRING, + Value: n.value.(string), + } + case NODE_LIST: + return &ast.List{ + Lsquare: pos, + Values: newValues(*n), + } + + case NODE_DICT: + var kvs []ast.KeyValue + for _, nn := range n.value.([]interface{}) { + kv := nn.([]interface{}) + k := NewNode(kv[0].(*VimNode)) + v := NewNode(kv[1].(*VimNode)) + kvs = append(kvs, ast.KeyValue{Key: k, Value: v}) + } + return &ast.Dict{ + Lcurlybrace: pos, + Entries: kvs, + } + + case NODE_OPTION: + return &ast.BasicLit{ + ValuePos: pos, + Kind: token.OPTION, + Value: n.value.(string), + } + case NODE_IDENTIFIER: + return &ast.Ident{ + NamePos: pos, + Name: n.value.(string), + } + + case NODE_CURLYNAME: + var parts []ast.CurlyNamePart + for _, n := range n.value.([]*VimNode) { + node := NewNode(n) + parts = append(parts, node.(ast.CurlyNamePart)) + } + return &ast.CurlyName{ + CurlyName: pos, + Parts: parts, + } + + case NODE_ENV: + return &ast.BasicLit{ + ValuePos: pos, + Kind: token.ENV, + Value: n.value.(string), + } + + case NODE_REG: + return &ast.BasicLit{ + ValuePos: pos, + Kind: token.REG, + Value: n.value.(string), + } + + case NODE_CURLYNAMEPART: + return &ast.CurlyNameLit{ + CurlyNameLit: pos, + Value: n.value.(string), + } + + case NODE_CURLYNAMEEXPR: + n := n.value.(*VimNode) + return &ast.CurlyNameExpr{ + CurlyNameExpr: pos, + Value: NewNode(n), + } + + case NODE_LAMBDA: + return &ast.LambdaExpr{ + Lcurlybrace: pos, + Params: newIdents(*n), + Expr: NewNode(n.left), + } + + } + panic(fmt.Errorf("Unknown node type: %v, node: %v", n.type_, n)) +} + +func newPos(p *pos) *ast.Pos { + if p == nil { + return nil + } + return &ast.Pos{ + Offset: p.i, + Line: p.lnum, + Column: p.col, + } +} + +func newExArg(ea ExArg) ast.ExArg { + return ast.ExArg{ Forceit: ea.forceit, AddrCount: ea.addr_count, Line1: ea.line1, @@ -48,10 +476,10 @@ func NewExportExArg(ea *ExArg) *ExportExArg { ForceFf: ea.force_ff, ForceEnc: ea.force_enc, BadChar: ea.bad_char, - Linepos: NewExportPos(ea.linepos), - Cmdpos: NewExportPos(ea.cmdpos), - Argpos: NewExportPos(ea.argpos), - Cmd: NewExportCmd(ea.cmd), + Linepos: newPos(ea.linepos), + Cmdpos: newPos(ea.cmdpos), + Argpos: newPos(ea.argpos), + Cmd: newCmd(ea.cmd), Modifiers: ea.modifiers, Range: ea.range_, Argopt: ea.argopt, @@ -59,18 +487,11 @@ func NewExportExArg(ea *ExArg) *ExportExArg { } } -type ExportCmd struct { - Name string - Minlen int - Flags string - Parser string -} - -func NewExportCmd(c *Cmd) *ExportCmd { +func newCmd(c *Cmd) *ast.Cmd { if c == nil { return nil } - return &ExportCmd{ + return &ast.Cmd{ Name: c.name, Minlen: c.minlen, Flags: c.flags, @@ -78,272 +499,151 @@ func NewExportCmd(c *Cmd) *ExportCmd { } } -type ExportPos struct { - I int - Lnum int - Col int -} - -func NewExportPos(p *pos) *ExportPos { - if p == nil { - return nil +func newBody(n VimNode) []ast.Statement { + var body []ast.Statement + if n.body != nil { + body = make([]ast.Statement, 0, len(n.body)) } - return &ExportPos{ - I: p.i, - Lnum: p.lnum, - Col: p.col, + for _, node := range n.body { + if node != nil { // conservative + body = append(body, NewNode(node)) + } } + return body } -type ExportNode struct { - Type int - Pos *ExportPos - Left *ExportNode - Right *ExportNode - Cond *ExportNode - Rest *ExportNode - List []*ExportNode - Rlist []*ExportNode - Body []*ExportNode - Op string - Str string - Depth int - Value interface{} - - Ea *ExportExArg - Attr *ExportFuncAttr - - Endfunction *ExportNode - Elseif []*ExportNode - Else *ExportNode - Endif *ExportNode - Endwhile *ExportNode - Endfor *ExportNode - Endtry *ExportNode - - Catch []*ExportNode - Finally *ExportNode - - Pattern string - Curly bool -} - -func NewExportNode(n *VimNode) *ExportNode { - if n == nil { - return nil - } - list := make([]*ExportNode, 0, len(n.list)) - for _, n := range n.list { - list = append(list, NewExportNode(n)) - } - rlist := make([]*ExportNode, 0, len(n.rlist)) - for _, n := range n.rlist { - rlist = append(rlist, NewExportNode(n)) - } - body := make([]*ExportNode, 0, len(n.body)) - for _, n := range n.body { - body = append(body, NewExportNode(n)) - } - elseif := make([]*ExportNode, 0, len(n.elseif)) - for _, n := range n.elseif { - elseif = append(elseif, NewExportNode(n)) - } - catch := make([]*ExportNode, 0, len(n.catch)) - for _, n := range n.catch { - catch = append(catch, NewExportNode(n)) +func newIdents(n VimNode) []ast.Ident { + var idents []ast.Ident + if n.rlist != nil { + idents = make([]ast.Ident, 0, len(n.rlist)) } - return &ExportNode{ - Type: n.type_, - Pos: NewExportPos(n.pos), - Left: NewExportNode(n.left), - Right: NewExportNode(n.right), - Cond: NewExportNode(n.cond), - Rest: NewExportNode(n.rest), - List: list, - Rlist: rlist, - Body: body, - Op: n.op, - Str: n.str, - Depth: n.depth, - Value: n.value, - - Ea: NewExportExArg(n.ea), - Attr: NewExportFuncAttr(n.attr), - - Endfunction: NewExportNode(n.endfunction), - Elseif: elseif, - Else: NewExportNode(n.else_), - Endif: NewExportNode(n.endif), - Endwhile: NewExportNode(n.endwhile), - Endfor: NewExportNode(n.endfor), - Endtry: NewExportNode(n.endtry), - - Catch: catch, - Finally: NewExportNode(n.finally), - - Pattern: n.pattern, - Curly: n.curly, + for _, node := range n.rlist { + if node != nil { // conservative + idents = append(idents, *NewNode(node).(*ast.Ident)) + } } + return idents } -type ExportFuncAttr struct { - Range bool - Abort bool - Dict bool - Closure bool -} - -func NewExportFuncAttr(attr *FuncAttr) *ExportFuncAttr { - if attr == nil { - return nil +func newRlist(n VimNode) []ast.Expr { + var exprs []ast.Expr + if n.rlist != nil { + exprs = make([]ast.Expr, 0, len(n.rlist)) } - return &ExportFuncAttr{ - Range: attr.range_, - Abort: attr.abort, - Dict: attr.dict, - Closure: attr.closure, + for _, node := range n.rlist { + if node != nil { // conservative + exprs = append(exprs, NewNode(node)) + } } + return exprs } -func (self *VimLParser) Parse(reader *StringReader) *ExportNode { - return NewExportNode(self.parse(reader)) -} - -func (self *ExprParser) Parse() *ExportNode { - return NewExportNode(self.parse()) -} - -func (self *Compiler) Compile(node *ExportNode) []string { - out := NewCompiler().compile(newInternalNode(node)) - if r, ok := out.([]string); ok { - return r +func newList(n VimNode) []ast.Expr { + var list []ast.Expr + if n.list != nil { + list = make([]ast.Expr, 0, len(n.list)) } - if r, ok := out.(string); ok { - return []string{r} + for _, node := range n.list { + if node != nil { // conservative + list = append(list, NewNode(node)) + } } - return nil + return list } -func newInternalNode(n *ExportNode) *VimNode { - if n == nil { - return nil - } - list := make([]*VimNode, 0, len(n.List)) - for _, n := range n.List { - list = append(list, newInternalNode(n)) - } - rlist := make([]*VimNode, 0, len(n.Rlist)) - for _, n := range n.Rlist { - rlist = append(rlist, newInternalNode(n)) - } - body := make([]*VimNode, 0, len(n.Body)) - for _, n := range n.Body { - body = append(body, newInternalNode(n)) - } - elseif := make([]*VimNode, 0, len(n.Elseif)) - for _, n := range n.Elseif { - elseif = append(elseif, newInternalNode(n)) - } - catch := make([]*VimNode, 0, len(n.Catch)) - for _, n := range n.Catch { - catch = append(catch, newInternalNode(n)) - } - return &VimNode{ - type_: n.Type, - pos: newInternalPos(n.Pos), - left: newInternalNode(n.Left), - right: newInternalNode(n.Right), - cond: newInternalNode(n.Cond), - rest: newInternalNode(n.Rest), - list: list, - rlist: rlist, - body: body, - op: n.Op, - str: n.Str, - depth: n.Depth, - value: n.Value, - - ea: newInternalExArg(n.Ea), - attr: newInternalFuncAttr(n.Attr), - - endfunction: newInternalNode(n.Endfunction), - elseif: elseif, - else_: newInternalNode(n.Else), - endif: newInternalNode(n.Endif), - endwhile: newInternalNode(n.Endwhile), - endfor: newInternalNode(n.Endfor), - endtry: newInternalNode(n.Endtry), - - catch: catch, - finally: newInternalNode(n.Finally), - - pattern: n.Pattern, - curly: n.Curly, - } -} - -func newInternalPos(p *ExportPos) *pos { - if p == nil { - return nil - } - return &pos{ - i: p.I, - lnum: p.Lnum, - col: p.Col, +func newValues(n VimNode) []ast.Expr { + var values []ast.Expr + for _, v := range n.value.([]interface{}) { + n := v.(*VimNode) + values = append(values, NewNode(n)) } + return values } -func newInternalFuncAttr(attr *ExportFuncAttr) *FuncAttr { - if attr == nil { - return nil - } - return &FuncAttr{ - range_: attr.Range, - abort: attr.Abort, - dict: attr.Dict, - } -} - -func newInternalExArg(ea *ExportExArg) *ExArg { - if ea == nil { - return nil - } - return &ExArg{ - forceit: ea.Forceit, - addr_count: ea.AddrCount, - line1: ea.Line1, - line2: ea.Line2, - flags: ea.Flags, - do_ecmd_cmd: ea.DoEcmdCmd, - do_ecmd_lnum: ea.DoEcmdLnum, - append: ea.Append, - usefilter: ea.Usefilter, - amount: ea.Amount, - regname: ea.Regname, - force_bin: ea.ForceBin, - read_edit: ea.ReadEdit, - force_ff: ea.ForceFf, - force_enc: ea.ForceEnc, - bad_char: ea.BadChar, - linepos: newInternalPos(ea.Linepos), - cmdpos: newInternalPos(ea.Cmdpos), - argpos: newInternalPos(ea.Argpos), - cmd: newInternalCmd(ea.Cmd), - modifiers: ea.Modifiers, - range_: ea.Range, - argopt: ea.Argopt, - argcmd: ea.Argcmd, - } -} - -func newInternalCmd(c *ExportCmd) *Cmd { - if c == nil { - return nil - } - return &Cmd{ - name: c.Name, - minlen: c.Minlen, - flags: c.Flags, - parser: c.Parser, - } +func opToken(nodeType int) token.Token { + switch nodeType { + case NODE_OR: + return token.OROR + case NODE_AND: + return token.ANDAND + case NODE_EQUAL: + return token.EQEQ + case NODE_EQUALCI: + return token.EQEQCI + case NODE_EQUALCS: + return token.EQEQCS + case NODE_NEQUAL: + return token.NEQ + case NODE_NEQUALCI: + return token.NEQCI + case NODE_NEQUALCS: + return token.NEQCS + case NODE_GREATER: + return token.GT + case NODE_GREATERCI: + return token.GTCI + case NODE_GREATERCS: + return token.GTCS + case NODE_GEQUAL: + return token.GTEQ + case NODE_GEQUALCI: + return token.GTEQCI + case NODE_GEQUALCS: + return token.GTEQCS + case NODE_SMALLER: + return token.LT + case NODE_SMALLERCI: + return token.LTCI + case NODE_SMALLERCS: + return token.LTCS + case NODE_SEQUAL: + return token.LTEQ + case NODE_SEQUALCI: + return token.LTEQCI + case NODE_SEQUALCS: + return token.LTEQCS + case NODE_MATCH: + return token.MATCH + case NODE_MATCHCI: + return token.MATCHCI + case NODE_MATCHCS: + return token.MATCHCS + case NODE_NOMATCH: + return token.NOMATCH + case NODE_NOMATCHCI: + return token.NOMATCHCI + case NODE_NOMATCHCS: + return token.NOMATCHCS + case NODE_IS: + return token.IS + case NODE_ISCI: + return token.ISCI + case NODE_ISCS: + return token.ISCS + case NODE_ISNOT: + return token.ISNOT + case NODE_ISNOTCI: + return token.ISNOTCI + case NODE_ISNOTCS: + return token.ISNOTCS + case NODE_ADD: + return token.PLUS + case NODE_SUBTRACT: + return token.MINUS + case NODE_CONCAT: + return token.DOT + case NODE_MULTIPLY: + return token.STAR + case NODE_DIVIDE: + return token.SLASH + case NODE_REMAINDER: + return token.PERCENT + case NODE_NOT: + return token.NOT + case NODE_MINUS: + return token.MINUS + case NODE_PLUS: + return token.PLUS + } + return token.ILLEGAL } diff --git a/internal/exporter/exarg.go b/internal/exporter/exarg.go deleted file mode 100644 index 0f2120c..0000000 --- a/internal/exporter/exarg.go +++ /dev/null @@ -1,45 +0,0 @@ -package exporter - -import "github.com/haya14busa/go-vimlparser/ast" -import internal "github.com/haya14busa/go-vimlparser/go" - -func newExArg(ea internal.ExportExArg) ast.ExArg { - return ast.ExArg{ - Forceit: ea.Forceit, - AddrCount: ea.AddrCount, - Line1: ea.Line1, - Line2: ea.Line2, - Flags: ea.Flags, - DoEcmdCmd: ea.DoEcmdCmd, - DoEcmdLnum: ea.DoEcmdLnum, - Append: ea.Append, - Usefilter: ea.Usefilter, - Amount: ea.Amount, - Regname: ea.Regname, - ForceBin: ea.ForceBin, - ReadEdit: ea.ReadEdit, - ForceFf: ea.ForceFf, - ForceEnc: ea.ForceEnc, - BadChar: ea.BadChar, - Linepos: newPos(ea.Linepos), - Cmdpos: newPos(ea.Cmdpos), - Argpos: newPos(ea.Argpos), - Cmd: newCmd(ea.Cmd), - Modifiers: ea.Modifiers, - Range: ea.Range, - Argopt: ea.Argopt, - Argcmd: ea.Argcmd, - } -} - -func newCmd(c *internal.ExportCmd) *ast.Cmd { - if c == nil { - return nil - } - return &ast.Cmd{ - Name: c.Name, - Minlen: c.Minlen, - Flags: c.Flags, - Parser: c.Parser, - } -} diff --git a/internal/exporter/node.go b/internal/exporter/node.go deleted file mode 100644 index 95a9999..0000000 --- a/internal/exporter/node.go +++ /dev/null @@ -1,501 +0,0 @@ -// Package exporter provides the way to export internal type to public one. -package exporter - -import ( - "fmt" - - "github.com/haya14busa/go-vimlparser/ast" - internal "github.com/haya14busa/go-vimlparser/go" - "github.com/haya14busa/go-vimlparser/token" -) - -// NewNode converts internal node type to ast.Node. -// n.Type must no be zero value. -// n.Pos must no be nil except TOPLEVEL node. -func NewNode(n *internal.ExportNode) ast.Node { - if n == nil { - return nil - } - - // TOPLEVEL doens't have position...? - var pos ast.Pos - if p := newPos(n.Pos); p != nil { - pos = *p - } else { - pos = ast.Pos{Offset: 0, Line: 1, Column: 1} - } - - switch n.Type { - - case internal.NODE_TOPLEVEL: - return &ast.File{Start: pos, Body: newBody(*n)} - - case internal.NODE_COMMENT: - return &ast.Comment{ - Quote: pos, - Text: n.Str, - } - - case internal.NODE_EXCMD: - return &ast.Excmd{ - Excmd: pos, - ExArg: newExArg(*n.Ea), - Command: n.Str, - } - - case internal.NODE_FUNCTION: - attr := ast.FuncAttr{} - if n.Attr != nil { - attr = ast.FuncAttr{ - Range: n.Attr.Range, - Abort: n.Attr.Abort, - Dict: n.Attr.Dict, - Closure: n.Attr.Closure, - } - } - return &ast.Function{ - Func: pos, - ExArg: newExArg(*n.Ea), - Body: newBody(*n), - Name: NewNode(n.Left), - Params: newIdents(*n), - Attr: attr, - EndFunction: *NewNode(n.Endfunction).(*ast.EndFunction), - } - - case internal.NODE_ENDFUNCTION: - return &ast.EndFunction{ - EndFunc: pos, - ExArg: newExArg(*n.Ea), - } - - case internal.NODE_DELFUNCTION: - return &ast.DelFunction{ - DelFunc: pos, - ExArg: newExArg(*n.Ea), - Name: NewNode(n.Left), - } - - case internal.NODE_RETURN: - return &ast.Return{ - Return: pos, - ExArg: newExArg(*n.Ea), - Result: NewNode(n.Left), - } - - case internal.NODE_EXCALL: - return &ast.ExCall{ - ExCall: pos, - ExArg: newExArg(*n.Ea), - FuncCall: *NewNode(n.Left).(*ast.CallExpr), - } - - case internal.NODE_LET: - return &ast.Let{ - Let: pos, - ExArg: newExArg(*n.Ea), - Op: n.Op, - Left: NewNode(n.Left), - List: newList(*n), - Rest: NewNode(n.Rest), - Right: NewNode(n.Right), - } - - case internal.NODE_UNLET: - return &ast.UnLet{ - UnLet: pos, - ExArg: newExArg(*n.Ea), - List: newList(*n), - } - - case internal.NODE_LOCKVAR: - return &ast.LockVar{ - LockVar: pos, - ExArg: newExArg(*n.Ea), - Depth: n.Depth, - List: newList(*n), - } - - case internal.NODE_UNLOCKVAR: - return &ast.UnLockVar{ - UnLockVar: pos, - ExArg: newExArg(*n.Ea), - Depth: n.Depth, - List: newList(*n), - } - - case internal.NODE_IF: - var elifs []ast.ElseIf - if n.Elseif != nil { - elifs = make([]ast.ElseIf, 0, len(n.Elseif)) - } - for _, node := range n.Elseif { - if node != nil { // conservative - elifs = append(elifs, *NewNode(node).(*ast.ElseIf)) - } - } - var els *ast.Else - if n.Else != nil { - els = NewNode(n.Else).(*ast.Else) - } - return &ast.If{ - If: pos, - ExArg: newExArg(*n.Ea), - Body: newBody(*n), - Condition: NewNode(n.Cond), - ElseIf: elifs, - Else: els, - EndIf: *NewNode(n.Endif).(*ast.EndIf), - } - - case internal.NODE_ELSEIF: - return &ast.ElseIf{ - ElseIf: pos, - ExArg: newExArg(*n.Ea), - Body: newBody(*n), - Condition: NewNode(n.Cond), - } - - case internal.NODE_ELSE: - return &ast.Else{ - Else: pos, - ExArg: newExArg(*n.Ea), - Body: newBody(*n), - } - - case internal.NODE_ENDIF: - return &ast.EndIf{ - EndIf: pos, - ExArg: newExArg(*n.Ea), - } - - case internal.NODE_WHILE: - return &ast.While{ - While: pos, - ExArg: newExArg(*n.Ea), - Body: newBody(*n), - Condition: NewNode(n.Cond), - EndWhile: *NewNode(n.Endwhile).(*ast.EndWhile), - } - - case internal.NODE_ENDWHILE: - return &ast.EndWhile{ - EndWhile: pos, - ExArg: newExArg(*n.Ea), - } - - case internal.NODE_FOR: - return &ast.For{ - For: pos, - ExArg: newExArg(*n.Ea), - Body: newBody(*n), - Left: NewNode(n.Left), - List: newList(*n), - Rest: NewNode(n.Rest), - Right: NewNode(n.Right), - EndFor: *NewNode(n.Endfor).(*ast.EndFor), - } - - case internal.NODE_ENDFOR: - return &ast.EndFor{ - EndFor: pos, - ExArg: newExArg(*n.Ea), - } - - case internal.NODE_CONTINUE: - return &ast.Continue{ - Continue: pos, - ExArg: newExArg(*n.Ea), - } - - case internal.NODE_BREAK: - return &ast.Break{ - Break: pos, - ExArg: newExArg(*n.Ea), - } - - case internal.NODE_TRY: - var catches []ast.Catch - if n.Catch != nil { - catches = make([]ast.Catch, 0, len(n.Catch)) - } - for _, node := range n.Catch { - if node != nil { // conservative - catches = append(catches, *NewNode(node).(*ast.Catch)) - } - } - var finally *ast.Finally - if n.Finally != nil { - finally = NewNode(n.Finally).(*ast.Finally) - } - return &ast.Try{ - Try: pos, - ExArg: newExArg(*n.Ea), - Body: newBody(*n), - Catch: catches, - Finally: finally, - EndTry: *NewNode(n.Endtry).(*ast.EndTry), - } - - case internal.NODE_CATCH: - return &ast.Catch{ - Catch: pos, - ExArg: newExArg(*n.Ea), - Body: newBody(*n), - Pattern: n.Pattern, - } - - case internal.NODE_FINALLY: - return &ast.Finally{ - Finally: pos, - ExArg: newExArg(*n.Ea), - Body: newBody(*n), - } - - case internal.NODE_ENDTRY: - return &ast.EndTry{ - EndTry: pos, - ExArg: newExArg(*n.Ea), - } - - case internal.NODE_THROW: - return &ast.Throw{ - Throw: pos, - ExArg: newExArg(*n.Ea), - Expr: NewNode(n.Left), - } - - case internal.NODE_ECHO, internal.NODE_ECHON, internal.NODE_ECHOMSG, internal.NODE_ECHOERR: - return &ast.EchoCmd{ - Start: pos, - CmdName: n.Ea.Cmd.Name, - ExArg: newExArg(*n.Ea), - Exprs: newList(*n), - } - - case internal.NODE_ECHOHL: - return &ast.Echohl{ - Echohl: pos, - ExArg: newExArg(*n.Ea), - Name: n.Str, - } - - case internal.NODE_EXECUTE: - return &ast.Execute{ - Execute: pos, - ExArg: newExArg(*n.Ea), - Exprs: newList(*n), - } - - case internal.NODE_TERNARY: - return &ast.TernaryExpr{ - Ternary: pos, - Condition: NewNode(n.Cond), - Left: NewNode(n.Left), - Right: NewNode(n.Right), - } - - case internal.NODE_OR, internal.NODE_AND, internal.NODE_EQUAL, internal.NODE_EQUALCI, internal.NODE_EQUALCS, - internal.NODE_NEQUAL, internal.NODE_NEQUALCI, internal.NODE_NEQUALCS, internal.NODE_GREATER, - internal.NODE_GREATERCI, internal.NODE_GREATERCS, internal.NODE_GEQUAL, internal.NODE_GEQUALCI, - internal.NODE_GEQUALCS, internal.NODE_SMALLER, internal.NODE_SMALLERCI, internal.NODE_SMALLERCS, - internal.NODE_SEQUAL, internal.NODE_SEQUALCI, internal.NODE_SEQUALCS, internal.NODE_MATCH, - internal.NODE_MATCHCI, internal.NODE_MATCHCS, internal.NODE_NOMATCH, internal.NODE_NOMATCHCI, - internal.NODE_NOMATCHCS, internal.NODE_IS, internal.NODE_ISCI, internal.NODE_ISCS, internal.NODE_ISNOT, - internal.NODE_ISNOTCI, internal.NODE_ISNOTCS, internal.NODE_ADD, internal.NODE_SUBTRACT, internal.NODE_CONCAT, - internal.NODE_MULTIPLY, internal.NODE_DIVIDE, internal.NODE_REMAINDER: - return &ast.BinaryExpr{ - Left: NewNode(n.Left), - OpPos: pos, - Op: opToken(n.Type), - Right: NewNode(n.Right), - } - - case internal.NODE_NOT, internal.NODE_MINUS, internal.NODE_PLUS: - return &ast.UnaryExpr{ - OpPos: pos, - Op: opToken(n.Type), - X: NewNode(n.Left), - } - - case internal.NODE_SUBSCRIPT: - return &ast.SubscriptExpr{ - Lbrack: pos, - Left: NewNode(n.Left), - Right: NewNode(n.Right), - } - - case internal.NODE_SLICE: - return &ast.SliceExpr{ - Lbrack: pos, - X: NewNode(n.Left), - Low: NewNode(n.Rlist[0]), - High: NewNode(n.Rlist[1]), - } - - case internal.NODE_CALL: - return &ast.CallExpr{ - Lparen: pos, - Fun: NewNode(n.Left), - Args: newRlist(*n), - } - - case internal.NODE_DOT: - return &ast.DotExpr{ - Left: NewNode(n.Left), - Dot: pos, - Right: *NewNode(n.Right).(*ast.Ident), - } - - case internal.NODE_NUMBER: - return &ast.BasicLit{ - ValuePos: pos, - Kind: token.NUMBER, - Value: n.Value.(string), - } - case internal.NODE_STRING: - return &ast.BasicLit{ - ValuePos: pos, - Kind: token.STRING, - Value: n.Value.(string), - } - case internal.NODE_LIST: - return &ast.List{ - Lsquare: pos, - Values: newValues(*n), - } - - case internal.NODE_DICT: - var kvs []ast.KeyValue - for _, nn := range n.Value.([]interface{}) { - kv := nn.([]interface{}) - k := NewNode(internal.NewExportNode(kv[0].(*internal.VimNode))) - v := NewNode(internal.NewExportNode(kv[1].(*internal.VimNode))) - kvs = append(kvs, ast.KeyValue{Key: k, Value: v}) - } - return &ast.Dict{ - Lcurlybrace: pos, - Entries: kvs, - } - - case internal.NODE_OPTION: - return &ast.BasicLit{ - ValuePos: pos, - Kind: token.OPTION, - Value: n.Value.(string), - } - case internal.NODE_IDENTIFIER: - return &ast.Ident{ - NamePos: pos, - Name: n.Value.(string), - } - - case internal.NODE_CURLYNAME: - var parts []ast.CurlyNamePart - for _, n := range n.Value.([]*internal.VimNode) { - node := NewNode(internal.NewExportNode(n)) - parts = append(parts, node.(ast.CurlyNamePart)) - } - return &ast.CurlyName{ - CurlyName: pos, - Parts: parts, - } - - case internal.NODE_ENV: - return &ast.BasicLit{ - ValuePos: pos, - Kind: token.ENV, - Value: n.Value.(string), - } - - case internal.NODE_REG: - return &ast.BasicLit{ - ValuePos: pos, - Kind: token.REG, - Value: n.Value.(string), - } - - case internal.NODE_CURLYNAMEPART: - return &ast.CurlyNameLit{ - CurlyNameLit: pos, - Value: n.Value.(string), - } - - case internal.NODE_CURLYNAMEEXPR: - n := n.Value.(*internal.VimNode) - return &ast.CurlyNameExpr{ - CurlyNameExpr: pos, - Value: NewNode(internal.NewExportNode(n)), - } - - case internal.NODE_LAMBDA: - return &ast.LambdaExpr{ - Lcurlybrace: pos, - Params: newIdents(*n), - Expr: NewNode(n.Left), - } - - } - panic(fmt.Errorf("Unknown node type: %v, node: %v", n.Type, n)) -} - -func newBody(n internal.ExportNode) []ast.Statement { - var body []ast.Statement - if n.Body != nil { - body = make([]ast.Statement, 0, len(n.Body)) - } - for _, node := range n.Body { - if node != nil { // conservative - body = append(body, NewNode(node)) - } - } - return body -} - -func newIdents(n internal.ExportNode) []ast.Ident { - var idents []ast.Ident - if n.Rlist != nil { - idents = make([]ast.Ident, 0, len(n.Rlist)) - } - for _, node := range n.Rlist { - if node != nil { // conservative - idents = append(idents, *NewNode(node).(*ast.Ident)) - } - } - return idents -} - -func newRlist(n internal.ExportNode) []ast.Expr { - var exprs []ast.Expr - if n.Rlist != nil { - exprs = make([]ast.Expr, 0, len(n.Rlist)) - } - for _, node := range n.Rlist { - if node != nil { // conservative - exprs = append(exprs, NewNode(node)) - } - } - return exprs -} - -func newList(n internal.ExportNode) []ast.Expr { - var list []ast.Expr - if n.List != nil { - list = make([]ast.Expr, 0, len(n.List)) - } - for _, node := range n.List { - if node != nil { // conservative - list = append(list, NewNode(node)) - } - } - return list -} - -func newValues(n internal.ExportNode) []ast.Expr { - var values []ast.Expr - for _, v := range n.Value.([]interface{}) { - n := v.(*internal.VimNode) - values = append(values, NewNode(internal.NewExportNode(n))) - } - return values -} diff --git a/internal/exporter/pos.go b/internal/exporter/pos.go deleted file mode 100644 index 107ba8a..0000000 --- a/internal/exporter/pos.go +++ /dev/null @@ -1,15 +0,0 @@ -package exporter - -import "github.com/haya14busa/go-vimlparser/ast" -import internal "github.com/haya14busa/go-vimlparser/go" - -func newPos(p *internal.ExportPos) *ast.Pos { - if p == nil { - return nil - } - return &ast.Pos{ - Offset: p.I, - Line: p.Lnum, - Column: p.Col, - } -} diff --git a/internal/exporter/token.go b/internal/exporter/token.go deleted file mode 100644 index 87ac16d..0000000 --- a/internal/exporter/token.go +++ /dev/null @@ -1,94 +0,0 @@ -package exporter - -import ( - internal "github.com/haya14busa/go-vimlparser/go" - "github.com/haya14busa/go-vimlparser/token" -) - -func opToken(nodeType int) token.Token { - switch nodeType { - case internal.NODE_OR: - return token.OROR - case internal.NODE_AND: - return token.ANDAND - case internal.NODE_EQUAL: - return token.EQEQ - case internal.NODE_EQUALCI: - return token.EQEQCI - case internal.NODE_EQUALCS: - return token.EQEQCS - case internal.NODE_NEQUAL: - return token.NEQ - case internal.NODE_NEQUALCI: - return token.NEQCI - case internal.NODE_NEQUALCS: - return token.NEQCS - case internal.NODE_GREATER: - return token.GT - case internal.NODE_GREATERCI: - return token.GTCI - case internal.NODE_GREATERCS: - return token.GTCS - case internal.NODE_GEQUAL: - return token.GTEQ - case internal.NODE_GEQUALCI: - return token.GTEQCI - case internal.NODE_GEQUALCS: - return token.GTEQCS - case internal.NODE_SMALLER: - return token.LT - case internal.NODE_SMALLERCI: - return token.LTCI - case internal.NODE_SMALLERCS: - return token.LTCS - case internal.NODE_SEQUAL: - return token.LTEQ - case internal.NODE_SEQUALCI: - return token.LTEQCI - case internal.NODE_SEQUALCS: - return token.LTEQCS - case internal.NODE_MATCH: - return token.MATCH - case internal.NODE_MATCHCI: - return token.MATCHCI - case internal.NODE_MATCHCS: - return token.MATCHCS - case internal.NODE_NOMATCH: - return token.NOMATCH - case internal.NODE_NOMATCHCI: - return token.NOMATCHCI - case internal.NODE_NOMATCHCS: - return token.NOMATCHCS - case internal.NODE_IS: - return token.IS - case internal.NODE_ISCI: - return token.ISCI - case internal.NODE_ISCS: - return token.ISCS - case internal.NODE_ISNOT: - return token.ISNOT - case internal.NODE_ISNOTCI: - return token.ISNOTCI - case internal.NODE_ISNOTCS: - return token.ISNOTCS - case internal.NODE_ADD: - return token.PLUS - case internal.NODE_SUBTRACT: - return token.MINUS - case internal.NODE_CONCAT: - return token.DOT - case internal.NODE_MULTIPLY: - return token.STAR - case internal.NODE_DIVIDE: - return token.SLASH - case internal.NODE_REMAINDER: - return token.PERCENT - case internal.NODE_NOT: - return token.NOT - case internal.NODE_MINUS: - return token.MINUS - case internal.NODE_PLUS: - return token.PLUS - } - return token.ILLEGAL -} diff --git a/vimlparser.go b/vimlparser.go index 2dea5e0..a2ef0c9 100644 --- a/vimlparser.go +++ b/vimlparser.go @@ -7,7 +7,6 @@ import ( "github.com/haya14busa/go-vimlparser/ast" internal "github.com/haya14busa/go-vimlparser/go" - "github.com/haya14busa/go-vimlparser/internal/exporter" ) // ErrVimlParser represents VimLParser error. @@ -57,7 +56,7 @@ func ParseFile(r io.Reader, filename string, opt *ParseOption) (node *ast.File, if opt != nil { neovim = opt.Neovim } - node = exporter.NewNode(internal.NewVimLParser(neovim).Parse(reader)).(*ast.File) + node = internal.NewVimLParser(neovim).Parse(reader).(*ast.File) return } @@ -82,7 +81,7 @@ func ParseExpr(r io.Reader) (node ast.Expr, err error) { lines := readlines(r) reader := internal.NewStringReader(lines) p := internal.NewExprParser(reader) - node = exporter.NewNode(p.Parse()) + node = p.Parse() return } From b12279c3cd328ed4117397171e9ee5ae78492604 Mon Sep 17 00:00:00 2001 From: haya14busa Date: Tue, 20 Sep 2016 03:17:47 +0900 Subject: [PATCH 4/8] s/NewNode/newAstNode --- go/export.go | 102 +++++++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/go/export.go b/go/export.go index 4827ec3..a9f71ae 100644 --- a/go/export.go +++ b/go/export.go @@ -8,19 +8,19 @@ import ( ) func (self *VimLParser) Parse(reader *StringReader) ast.Node { - return NewNode(self.parse(reader)) + return newAstNode(self.parse(reader)) } func (self *ExprParser) Parse() ast.Node { - return NewNode(self.parse()) + return newAstNode(self.parse()) } // ---- -// NewNode converts internal node type to ast.Node. +// newAstNode converts internal node type to ast.Node. // n.type_ must no be zero value. // n.pos must no be nil except TOPLEVEL node. -func NewNode(n *VimNode) ast.Node { +func newAstNode(n *VimNode) ast.Node { if n == nil { return nil } @@ -65,10 +65,10 @@ func NewNode(n *VimNode) ast.Node { Func: pos, ExArg: newExArg(*n.ea), Body: newBody(*n), - Name: NewNode(n.left), + Name: newAstNode(n.left), Params: newIdents(*n), Attr: attr, - EndFunction: *NewNode(n.endfunction).(*ast.EndFunction), + EndFunction: *newAstNode(n.endfunction).(*ast.EndFunction), } case NODE_ENDFUNCTION: @@ -81,21 +81,21 @@ func NewNode(n *VimNode) ast.Node { return &ast.DelFunction{ DelFunc: pos, ExArg: newExArg(*n.ea), - Name: NewNode(n.left), + Name: newAstNode(n.left), } case NODE_RETURN: return &ast.Return{ Return: pos, ExArg: newExArg(*n.ea), - Result: NewNode(n.left), + Result: newAstNode(n.left), } case NODE_EXCALL: return &ast.ExCall{ ExCall: pos, ExArg: newExArg(*n.ea), - FuncCall: *NewNode(n.left).(*ast.CallExpr), + FuncCall: *newAstNode(n.left).(*ast.CallExpr), } case NODE_LET: @@ -103,10 +103,10 @@ func NewNode(n *VimNode) ast.Node { Let: pos, ExArg: newExArg(*n.ea), Op: n.op, - Left: NewNode(n.left), + Left: newAstNode(n.left), List: newList(*n), - Rest: NewNode(n.rest), - Right: NewNode(n.right), + Rest: newAstNode(n.rest), + Right: newAstNode(n.right), } case NODE_UNLET: @@ -139,21 +139,21 @@ func NewNode(n *VimNode) ast.Node { } for _, node := range n.elseif { if node != nil { // conservative - elifs = append(elifs, *NewNode(node).(*ast.ElseIf)) + elifs = append(elifs, *newAstNode(node).(*ast.ElseIf)) } } var els *ast.Else if n.else_ != nil { - els = NewNode(n.else_).(*ast.Else) + els = newAstNode(n.else_).(*ast.Else) } return &ast.If{ If: pos, ExArg: newExArg(*n.ea), Body: newBody(*n), - Condition: NewNode(n.cond), + Condition: newAstNode(n.cond), ElseIf: elifs, Else: els, - EndIf: *NewNode(n.endif).(*ast.EndIf), + EndIf: *newAstNode(n.endif).(*ast.EndIf), } case NODE_ELSEIF: @@ -161,7 +161,7 @@ func NewNode(n *VimNode) ast.Node { ElseIf: pos, ExArg: newExArg(*n.ea), Body: newBody(*n), - Condition: NewNode(n.cond), + Condition: newAstNode(n.cond), } case NODE_ELSE: @@ -182,8 +182,8 @@ func NewNode(n *VimNode) ast.Node { While: pos, ExArg: newExArg(*n.ea), Body: newBody(*n), - Condition: NewNode(n.cond), - EndWhile: *NewNode(n.endwhile).(*ast.EndWhile), + Condition: newAstNode(n.cond), + EndWhile: *newAstNode(n.endwhile).(*ast.EndWhile), } case NODE_ENDWHILE: @@ -197,11 +197,11 @@ func NewNode(n *VimNode) ast.Node { For: pos, ExArg: newExArg(*n.ea), Body: newBody(*n), - Left: NewNode(n.left), + Left: newAstNode(n.left), List: newList(*n), - Rest: NewNode(n.rest), - Right: NewNode(n.right), - EndFor: *NewNode(n.endfor).(*ast.EndFor), + Rest: newAstNode(n.rest), + Right: newAstNode(n.right), + EndFor: *newAstNode(n.endfor).(*ast.EndFor), } case NODE_ENDFOR: @@ -229,12 +229,12 @@ func NewNode(n *VimNode) ast.Node { } for _, node := range n.catch { if node != nil { // conservative - catches = append(catches, *NewNode(node).(*ast.Catch)) + catches = append(catches, *newAstNode(node).(*ast.Catch)) } } var finally *ast.Finally if n.finally != nil { - finally = NewNode(n.finally).(*ast.Finally) + finally = newAstNode(n.finally).(*ast.Finally) } return &ast.Try{ Try: pos, @@ -242,7 +242,7 @@ func NewNode(n *VimNode) ast.Node { Body: newBody(*n), Catch: catches, Finally: finally, - EndTry: *NewNode(n.endtry).(*ast.EndTry), + EndTry: *newAstNode(n.endtry).(*ast.EndTry), } case NODE_CATCH: @@ -270,7 +270,7 @@ func NewNode(n *VimNode) ast.Node { return &ast.Throw{ Throw: pos, ExArg: newExArg(*n.ea), - Expr: NewNode(n.left), + Expr: newAstNode(n.left), } case NODE_ECHO, NODE_ECHON, NODE_ECHOMSG, NODE_ECHOERR: @@ -298,9 +298,9 @@ func NewNode(n *VimNode) ast.Node { case NODE_TERNARY: return &ast.TernaryExpr{ Ternary: pos, - Condition: NewNode(n.cond), - Left: NewNode(n.left), - Right: NewNode(n.right), + Condition: newAstNode(n.cond), + Left: newAstNode(n.left), + Right: newAstNode(n.right), } case NODE_OR, NODE_AND, NODE_EQUAL, NODE_EQUALCI, NODE_EQUALCS, @@ -313,46 +313,46 @@ func NewNode(n *VimNode) ast.Node { NODE_ISNOTCI, NODE_ISNOTCS, NODE_ADD, NODE_SUBTRACT, NODE_CONCAT, NODE_MULTIPLY, NODE_DIVIDE, NODE_REMAINDER: return &ast.BinaryExpr{ - Left: NewNode(n.left), + Left: newAstNode(n.left), OpPos: pos, Op: opToken(n.type_), - Right: NewNode(n.right), + Right: newAstNode(n.right), } case NODE_NOT, NODE_MINUS, NODE_PLUS: return &ast.UnaryExpr{ OpPos: pos, Op: opToken(n.type_), - X: NewNode(n.left), + X: newAstNode(n.left), } case NODE_SUBSCRIPT: return &ast.SubscriptExpr{ Lbrack: pos, - Left: NewNode(n.left), - Right: NewNode(n.right), + Left: newAstNode(n.left), + Right: newAstNode(n.right), } case NODE_SLICE: return &ast.SliceExpr{ Lbrack: pos, - X: NewNode(n.left), - Low: NewNode(n.rlist[0]), - High: NewNode(n.rlist[1]), + X: newAstNode(n.left), + Low: newAstNode(n.rlist[0]), + High: newAstNode(n.rlist[1]), } case NODE_CALL: return &ast.CallExpr{ Lparen: pos, - Fun: NewNode(n.left), + Fun: newAstNode(n.left), Args: newRlist(*n), } case NODE_DOT: return &ast.DotExpr{ - Left: NewNode(n.left), + Left: newAstNode(n.left), Dot: pos, - Right: *NewNode(n.right).(*ast.Ident), + Right: *newAstNode(n.right).(*ast.Ident), } case NODE_NUMBER: @@ -377,8 +377,8 @@ func NewNode(n *VimNode) ast.Node { var kvs []ast.KeyValue for _, nn := range n.value.([]interface{}) { kv := nn.([]interface{}) - k := NewNode(kv[0].(*VimNode)) - v := NewNode(kv[1].(*VimNode)) + k := newAstNode(kv[0].(*VimNode)) + v := newAstNode(kv[1].(*VimNode)) kvs = append(kvs, ast.KeyValue{Key: k, Value: v}) } return &ast.Dict{ @@ -401,7 +401,7 @@ func NewNode(n *VimNode) ast.Node { case NODE_CURLYNAME: var parts []ast.CurlyNamePart for _, n := range n.value.([]*VimNode) { - node := NewNode(n) + node := newAstNode(n) parts = append(parts, node.(ast.CurlyNamePart)) } return &ast.CurlyName{ @@ -433,14 +433,14 @@ func NewNode(n *VimNode) ast.Node { n := n.value.(*VimNode) return &ast.CurlyNameExpr{ CurlyNameExpr: pos, - Value: NewNode(n), + Value: newAstNode(n), } case NODE_LAMBDA: return &ast.LambdaExpr{ Lcurlybrace: pos, Params: newIdents(*n), - Expr: NewNode(n.left), + Expr: newAstNode(n.left), } } @@ -506,7 +506,7 @@ func newBody(n VimNode) []ast.Statement { } for _, node := range n.body { if node != nil { // conservative - body = append(body, NewNode(node)) + body = append(body, newAstNode(node)) } } return body @@ -519,7 +519,7 @@ func newIdents(n VimNode) []ast.Ident { } for _, node := range n.rlist { if node != nil { // conservative - idents = append(idents, *NewNode(node).(*ast.Ident)) + idents = append(idents, *newAstNode(node).(*ast.Ident)) } } return idents @@ -532,7 +532,7 @@ func newRlist(n VimNode) []ast.Expr { } for _, node := range n.rlist { if node != nil { // conservative - exprs = append(exprs, NewNode(node)) + exprs = append(exprs, newAstNode(node)) } } return exprs @@ -545,7 +545,7 @@ func newList(n VimNode) []ast.Expr { } for _, node := range n.list { if node != nil { // conservative - list = append(list, NewNode(node)) + list = append(list, newAstNode(node)) } } return list @@ -555,7 +555,7 @@ func newValues(n VimNode) []ast.Expr { var values []ast.Expr for _, v := range n.value.([]interface{}) { n := v.(*VimNode) - values = append(values, NewNode(n)) + values = append(values, newAstNode(n)) } return values } From 64a3a04ed59b13a4089e9f91bea587a595ac6d2c Mon Sep 17 00:00:00 2001 From: haya14busa Date: Tue, 20 Sep 2016 03:23:54 +0900 Subject: [PATCH 5/8] reduce unused Regexp compile --- go/vimlfunc.go | 15 --------------- go/vimlfunc_test.go | 46 --------------------------------------------- 2 files changed, 61 deletions(-) diff --git a/go/vimlfunc.go b/go/vimlfunc.go index 3ac2c38..65eeffd 100644 --- a/go/vimlfunc.go +++ b/go/vimlfunc.go @@ -66,12 +66,10 @@ var patVim2Go = map[string]string{ } var patVim2GoRegh = make(map[string]*regexp.Regexp) -var patVim2GoRegq = make(map[string]*regexp.Regexp) func init() { for k, v := range patVim2Go { patVim2GoRegh[k] = regexp.MustCompile(v) - patVim2GoRegq[k] = regexp.MustCompile("(?i)" + v) } } @@ -84,12 +82,6 @@ func viml_empty(obj interface{}) bool { func viml_equalci(a, b string) bool { return strings.ToLower(a) == strings.ToLower(b) } -func viml_eqreg(s, reg string) bool { - if r, ok := patVim2GoRegq[reg]; ok { - return r.MatchString(s) - } - panic(fmt.Errorf("NotImplemented viml_eqreg for %v", reg)) -} func viml_eqregh(s, reg string) bool { if r, ok := patVim2GoRegh[reg]; ok { @@ -98,13 +90,6 @@ func viml_eqregh(s, reg string) bool { panic("NotImplemented viml_eqregh") } -func viml_eqregq(s, reg string) bool { - if r, ok := patVim2GoRegq[reg]; ok { - return r.MatchString(s) - } - panic("NotImplemented viml_eqregq") -} - func viml_escape(s string, chars string) string { r := "" for _, c := range s { diff --git a/go/vimlfunc_test.go b/go/vimlfunc_test.go index 1019761..b46ffd4 100644 --- a/go/vimlfunc_test.go +++ b/go/vimlfunc_test.go @@ -5,34 +5,6 @@ import ( "testing" ) -func TestViml_eqreg(t *testing.T) { - tests := []struct { - in string - reg string - want bool - }{ - {in: ``, reg: "^\\s*\\\\", want: false}, - {in: `hoge`, reg: "^\\s*\\\\", want: false}, - {in: ` \ hoge`, reg: "^\\s*\\\\", want: true}, - {in: `\`, reg: "^\\s*\\\\", want: true}, - - // ^++ - {in: `++hoge`, reg: "^++", want: true}, - {in: `hoge`, reg: "^++", want: false}, - - // case - {in: `deletel`, reg: "\\v^d%[elete][lp]$", want: true}, - {in: `deleteL`, reg: "\\v^d%[elete][lp]$", want: true}, - {in: `++bad=keep`, reg: "^++bad=keep", want: true}, - {in: `++bad=KEEP`, reg: "^++bad=keep", want: true}, - } - for _, tt := range tests { - if got := viml_eqreg(tt.in, tt.reg); got != tt.want { - t.Errorf("viml_eqreg(%q, %q) = %v, want %v", tt.in, tt.reg, got, tt.want) - } - } -} - func TestViml_eqregh(t *testing.T) { tests := []struct { in string @@ -51,24 +23,6 @@ func TestViml_eqregh(t *testing.T) { } } -func TestViml_eqregq(t *testing.T) { - tests := []struct { - in string - reg string - want bool - }{ - {in: `deletel`, reg: "\\v^d%[elete][lp]$", want: true}, - {in: `deleteL`, reg: "\\v^d%[elete][lp]$", want: true}, - {in: `++bad=keep`, reg: "^++bad=keep", want: true}, - {in: `++bad=KEEP`, reg: "^++bad=keep", want: true}, - } - for _, tt := range tests { - if got := viml_eqregq(tt.in, tt.reg); got != tt.want { - t.Errorf("viml_eqregq(%q, %q) = %v, want %v", tt.in, tt.reg, got, tt.want) - } - } -} - func TestViml_printf(t *testing.T) { tests := []struct { f string From 3a2f2be0368ad8372b55910709f21f3ab4f8bd42 Mon Sep 17 00:00:00 2001 From: haya14busa Date: Tue, 20 Sep 2016 03:36:49 +0900 Subject: [PATCH 6/8] update benchmarks result in README --- README.mkd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.mkd b/README.mkd index 82aeeb4..3c7c364 100644 --- a/README.mkd +++ b/README.mkd @@ -45,7 +45,7 @@ node js/vimlparser.js autoload/vimlparser.vim > /dev/null 0.77s user 0.04s syst $ go get github.com/haya14busa/go-vimlparser/cmd/vimlparser $ time vimlparser autoload/vimlparser.vim > /dev/null -vimlparser autoload/vimlparser.vim > /dev/null 0.36s user 0.02s system 125% cpu 0.299 total +vimlparser autoload/vimlparser.vim > /dev/null 0.25s user 0.03s system 114% cpu 0.244 total ``` @@ -55,7 +55,7 @@ vimlparser autoload/vimlparser.vim > /dev/null 0.36s user 0.02s system 125% cpu | Python3 | 4.17s | | pypy3 | 2.63s | | node | 0.77s | -| Go | **0.36s** | +| Go | **0.25s** | Note that, in addition to the Go lang speed, I added [performance improvement](https://github.com/haya14busa/go-vimlparser/pull/4) for Go implementation. From 2ff688ad7563dfac58e63df93ba17647a0283a35 Mon Sep 17 00:00:00 2001 From: haya14busa Date: Tue, 20 Sep 2016 03:51:53 +0900 Subject: [PATCH 7/8] return nil for Node(-1) $ benchcmp prof.3 prof.4 benchmark old ns/op new ns/op delta BenchmarkParseFile-4 227103681 221732445 -2.37% --- go/type.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/go/type.go b/go/type.go index 6aaebc9..dd8ecd8 100644 --- a/go/type.go +++ b/go/type.go @@ -93,6 +93,9 @@ type pos struct { // Node returns new VimNode. func Node(type_ int) *VimNode { + if type_ == -1 { + return nil + } return &VimNode{ type_: type_, attr: &FuncAttr{}, From 47a8076be08eed57476b3ffe33aba80bc77995aa Mon Sep 17 00:00:00 2001 From: haya14busa Date: Tue, 20 Sep 2016 03:58:36 +0900 Subject: [PATCH 8/8] reduce slice allocation when NODE_DICT type conversion Before: . . 375: . . 376: case NODE_DICT: . . 377: var kvs []ast.KeyValue . . 378: for _, nn := range n.value.([]interface{}) { . . 379: kv := nn.([]interface{}) . 3MB 380: k := newAstNode(kv[0].(*VimNode)) . 2.50MB 381: v := newAstNode(kv[1].(*VimNode)) 5MB 5MB 382: kvs = append(kvs, ast.KeyValue{Key: k, Value: v}) . . 383: } . . 384: return &ast.Dict{ . . 385: Lcurlybrace: pos, . . 386: Entries: kvs, . . 387: } . . 388: After: . . 375: . . 376: case NODE_DICT: . . 377: entries := n.value.([]interface{}) 2MB 2MB 378: kvs := make([]ast.KeyValue, 0, len(entries)) . . 379: for _, nn := range entries { . . 380: kv := nn.([]interface{}) . 3.50MB 381: k := newAstNode(kv[0].(*VimNode)) . 3.50MB 382: v := newAstNode(kv[1].(*VimNode)) . . 383: kvs = append(kvs, ast.KeyValue{Key: k, Value: v}) . . 384: } . . 385: return &ast.Dict{ . . 386: Lcurlybrace: pos, 1MB 1MB 387: Entries: kvs, . . 388: } --- go/export.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/go/export.go b/go/export.go index a9f71ae..6f278cd 100644 --- a/go/export.go +++ b/go/export.go @@ -374,8 +374,9 @@ func newAstNode(n *VimNode) ast.Node { } case NODE_DICT: - var kvs []ast.KeyValue - for _, nn := range n.value.([]interface{}) { + entries := n.value.([]interface{}) + kvs := make([]ast.KeyValue, 0, len(entries)) + for _, nn := range entries { kv := nn.([]interface{}) k := newAstNode(kv[0].(*VimNode)) v := newAstNode(kv[1].(*VimNode))