diff --git a/.travis.yml b/.travis.yml index fdc8bd2..7a3b7f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,8 @@ language: go go: - - 1.8 + - 1.14 + - 1.15 - tip install: diff --git a/ast/node.go b/ast/node.go index 244a64a..cb0e026 100644 --- a/ast/node.go +++ b/ast/node.go @@ -65,6 +65,7 @@ type Function struct { Body []Statement // function body Name Expr // function name Params []*Ident // parameters + DefaultArgs []Expr // default arguments Attr FuncAttr // function attributes EndFunction *EndFunction // :endfunction } @@ -119,6 +120,7 @@ func (f *ExCall) Pos() Pos { return f.ExCall } func (f *ExCall) Cmd() Cmd { return *f.ExArg.Cmd } // vimlparser: LET .ea .op .left .list .rest .right +// vimlparser: CONST .ea .op .left .list .rest .right type Let struct { Let Pos // position of starting the :let ExArg ExArg // Ex command arg @@ -341,6 +343,17 @@ type Throw struct { func (f *Throw) Pos() Pos { return f.Throw } func (f *Throw) Cmd() Cmd { return *f.ExArg.Cmd } +// vimlparser: EVAL .ea .left +// :eval {Expr} +type Eval struct { + Eval Pos // position of starting the :eval + ExArg ExArg // Ex command arg + Expr Expr +} + +func (f *Eval) Pos() Pos { return f.Eval } +func (f *Eval) Cmd() Cmd { return *f.ExArg.Cmd } + // vimlparser: ECHO .ea .list // vimlparser: ECHON .ea .list // vimlparser: ECHOMSG .ea .list @@ -426,6 +439,16 @@ type SliceExpr struct { func (f *SliceExpr) Pos() Pos { return f.Lbrack } +// vimlparser: METHOD .left .right +type MethodExpr struct { + Left Expr // this object + Method Expr // method + Lparen Pos // position of "(" + Args []Expr // function arguments; or nil +} + +func (c *MethodExpr) Pos() Pos { return c.Lparen } + // vimlparser: CALL .left .rlist type CallExpr struct { Fun Expr // function expression @@ -516,7 +539,7 @@ type Ident struct { func (i *Ident) Pos() Pos { return i.NamePos } // LambdaExpr node represents lambda. -// vimlparsr: LAMBDA .rlist .left +// vimlparser: LAMBDA .rlist .left // { Params -> Expr } type LambdaExpr struct { Lcurlybrace Pos // position of "{" @@ -527,7 +550,7 @@ type LambdaExpr struct { func (i *LambdaExpr) Pos() Pos { return i.Lcurlybrace } // ParenExpr node represents a parenthesized expression. -// vimlparsr: PARENEXPR .value +// vimlparser: PARENEXPR .value type ParenExpr struct { Lparen Pos // position of "(" X Expr // parenthesized expression @@ -535,6 +558,17 @@ type ParenExpr struct { func (i *ParenExpr) Pos() Pos { return i.Lparen } +// HeredocExpr node represents a heredoc expression. +// vimlparser: HEREDOC .rlist .op .body +type HeredocExpr struct { + OpPos Pos // position of "=<<" + Flags []Expr // modifiers [trim]; or nil + EndMarker string // {endmarker} + Body []Expr // body +} + +func (i *HeredocExpr) Pos() Pos { return i.OpPos } + // stmtNode() ensures that only ExComamnd and Comment nodes can be assigned to // an Statement. // @@ -562,6 +596,7 @@ func (*Let) stmtNode() {} func (*LockVar) stmtNode() {} func (*Return) stmtNode() {} func (*Throw) stmtNode() {} +func (*Eval) stmtNode() {} func (*Try) stmtNode() {} func (*UnLet) stmtNode() {} func (*UnLockVar) stmtNode() {} @@ -576,6 +611,7 @@ func (*BinaryExpr) exprNode() {} func (*UnaryExpr) exprNode() {} func (*SubscriptExpr) exprNode() {} func (*SliceExpr) exprNode() {} +func (*MethodExpr) exprNode() {} func (*CallExpr) exprNode() {} func (*DotExpr) exprNode() {} func (*BasicLit) exprNode() {} @@ -587,3 +623,4 @@ func (*CurlyNameExpr) exprNode() {} func (*Ident) exprNode() {} func (*LambdaExpr) exprNode() {} func (*ParenExpr) exprNode() {} +func (*HeredocExpr) exprNode() {} diff --git a/ast/walk.go b/ast/walk.go index 62798bd..39d011a 100644 --- a/ast/walk.go +++ b/ast/walk.go @@ -135,6 +135,9 @@ func Walk(v Visitor, node Node) { case *Throw: Walk(v, n.Expr) + case *Eval: + Walk(v, n.Expr) + case *EchoCmd: walkExprList(v, n.Exprs) @@ -164,6 +167,11 @@ func Walk(v Visitor, node Node) { Walk(v, n.Low) Walk(v, n.High) + case *MethodExpr: + Walk(v, n.Left) + Walk(v, n.Method) + walkExprList(v, n.Args) + case *CallExpr: Walk(v, n.Fun) walkExprList(v, n.Args) @@ -201,6 +209,10 @@ func Walk(v Visitor, node Node) { case *ParenExpr: Walk(v, n.X) + case *HeredocExpr: + walkExprList(v, n.Flags) + walkExprList(v, n.Body) + default: panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n)) } diff --git a/autoload/vimlparser.vim b/autoload/vimlparser.vim index 3da9a84..a7ae041 100644 --- a/autoload/vimlparser.vim +++ b/autoload/vimlparser.vim @@ -4,21 +4,21 @@ " " License: This file is placed in the public domain. -function! vimlparser#import() +function! vimlparser#import() abort return s: endfunction " @brief Read input as VimScript and return stringified AST. " @param input Input filename or string of VimScript. " @return Stringified AST. -function! vimlparser#test(input, ...) +function! vimlparser#test(input, ...) abort try if a:0 > 0 let l:neovim = a:1 else let l:neovim = 0 endif - let i = type(a:input) == 1 && filereadable(a:input) ? readfile(a:input) : [a:input] + let i = type(a:input) ==# 1 && filereadable(a:input) ? readfile(a:input) : split(a:input, "\n") let r = s:StringReader.new(i) let p = s:VimLParser.new(l:neovim) let c = s:Compiler.new() @@ -28,12 +28,12 @@ function! vimlparser#test(input, ...) endtry endfunction -function! s:numtoname(num) +function! s:numtoname(num) abort let sig = printf("function('%s')", a:num) for k in keys(s:) - if type(s:[k]) == type({}) + if type(s:[k]) ==# type({}) for name in keys(s:[k]) - if type(s:[k][name]) == type(function('tr')) && string(s:[k][name]) == sig + if type(s:[k][name]) ==# type(function('tr')) && string(s:[k][name]) ==# sig return printf('%s.%s', k, name) endif endfor @@ -137,7 +137,12 @@ let s:NODE_REG = 89 let s:NODE_CURLYNAMEPART = 90 let s:NODE_CURLYNAMEEXPR = 91 let s:NODE_LAMBDA = 92 -let s:NODE_PARENEXPR = 93 +let s:NODE_BLOB = 93 +let s:NODE_CONST = 94 +let s:NODE_EVAL = 95 +let s:NODE_HEREDOC = 96 +let s:NODE_METHOD = 97 +let s:NODE_PARENEXPR = 200 let s:TOKEN_EOF = 1 let s:TOKEN_EOL = 2 @@ -204,71 +209,75 @@ let s:TOKEN_BACKTICK = 62 let s:TOKEN_DOTDOTDOT = 63 let s:TOKEN_SHARP = 64 let s:TOKEN_ARROW = 65 +let s:TOKEN_BLOB = 66 +let s:TOKEN_LITCOPEN = 67 +let s:TOKEN_DOTDOT = 68 +let s:TOKEN_HEREDOC = 69 let s:MAX_FUNC_ARGS = 20 -function! s:isalpha(c) +function! s:isalpha(c) abort return a:c =~# '^[A-Za-z]$' endfunction -function! s:isalnum(c) +function! s:isalnum(c) abort return a:c =~# '^[0-9A-Za-z]$' endfunction -function! s:isdigit(c) +function! s:isdigit(c) abort return a:c =~# '^[0-9]$' endfunction -function! s:isodigit(c) +function! s:isodigit(c) abort return a:c =~# '^[0-7]$' endfunction -function! s:isxdigit(c) +function! s:isxdigit(c) abort return a:c =~# '^[0-9A-Fa-f]$' endfunction -function! s:iswordc(c) +function! s:iswordc(c) abort return a:c =~# '^[0-9A-Za-z_]$' endfunction -function! s:iswordc1(c) +function! s:iswordc1(c) abort return a:c =~# '^[A-Za-z_]$' endfunction -function! s:iswhite(c) +function! s:iswhite(c) abort return a:c =~# '^[ \t]$' endfunction -function! s:isnamec(c) +function! s:isnamec(c) abort return a:c =~# '^[0-9A-Za-z_:#]$' endfunction -function! s:isnamec1(c) +function! s:isnamec1(c) abort return a:c =~# '^[A-Za-z_]$' endfunction -function! s:isargname(s) +function! s:isargname(s) abort return a:s =~# '^[A-Za-z_][0-9A-Za-z_]*$' endfunction -function! s:isvarname(s) +function! s:isvarname(s) abort return a:s =~# '^[vgslabwt]:$\|^\([vgslabwt]:\)\?[A-Za-z_][0-9A-Za-z_#]*$' endfunction " FIXME: -function! s:isidc(c) +function! s:isidc(c) abort return a:c =~# '^[0-9A-Za-z_]$' endfunction -function! s:isupper(c) +function! s:isupper(c) abort return a:c =~# '^[A-Z]$' endfunction -function! s:islower(c) +function! s:islower(c) abort return a:c =~# '^[a-z]$' endfunction -function! s:ExArg() +function! s:ExArg() abort let ea = {} let ea.forceit = s:FALSE let ea.addr_count = 0 @@ -306,6 +315,7 @@ endfunction " node rest " node[] list " node[] rlist +" node[] default_args " node[] body " string op " string str @@ -315,12 +325,13 @@ endfunction " TOPLEVEL .body " COMMENT .str " EXCMD .ea .str -" FUNCTION .ea .body .left .rlist .attr .endfunction +" FUNCTION .ea .body .left .rlist .default_args .attr .endfunction " ENDFUNCTION .ea " DELFUNCTION .ea .left " RETURN .ea .left " EXCALL .ea .left " LET .ea .op .left .list .rest .right +" CONST .ea .op .left .list .rest .right " UNLET .ea .list " LOCKVAR .ea .depth .list " UNLOCKVAR .ea .depth .list @@ -339,6 +350,7 @@ endfunction " FINALLY .ea .body " ENDTRY .ea " THROW .ea .left +" EVAL .ea .left " ECHO .ea .list " ECHON .ea .list " ECHOHL .ea .str @@ -389,12 +401,14 @@ endfunction " PLUS .left " SUBSCRIPT .left .right " SLICE .left .rlist +" METHOD .left .right " CALL .left .rlist " DOT .left .right " NUMBER .value " STRING .value " LIST .value " DICT .value +" BLOB .value " NESTING .left " OPTION .value " IDENTIFIER .value @@ -404,24 +418,25 @@ endfunction " CURLYNAMEPART .value " CURLYNAMEEXPR .value " LAMBDA .rlist .left +" HEREDOC .rlist .op .body " PARENEXPR .value -function! s:Node(type) +function! s:Node(type) abort return {'type': a:type} endfunction -function! s:Err(msg, pos) +function! s:Err(msg, pos) abort return printf('vimlparser: %s: line %d col %d', a:msg, a:pos.lnum, a:pos.col) endfunction let s:VimLParser = {} -function! s:VimLParser.new(...) +function! s:VimLParser.new(...) abort let obj = copy(self) call call(obj.__init__, a:000, obj) return obj endfunction -function! s:VimLParser.__init__(...) +function! s:VimLParser.__init__(...) abort if len(a:000) > 0 let self.neovim = a:000[0] else @@ -431,18 +446,18 @@ function! s:VimLParser.__init__(...) let self.find_command_cache = {} endfunction -function! s:VimLParser.push_context(node) +function! s:VimLParser.push_context(node) abort call insert(self.context, a:node) endfunction -function! s:VimLParser.pop_context() +function! s:VimLParser.pop_context() abort call remove(self.context, 0) endfunction -function! s:VimLParser.find_context(type) +function! s:VimLParser.find_context(type) abort let i = 0 for node in self.context - if node.type == a:type + if node.type ==# a:type return i endif let i += 1 @@ -450,44 +465,45 @@ function! s:VimLParser.find_context(type) return -1 endfunction -function! s:VimLParser.add_node(node) +function! s:VimLParser.add_node(node) abort call add(self.context[0].body, a:node) endfunction -function! s:VimLParser.check_missing_endfunction(ends, pos) - if self.context[0].type == s:NODE_FUNCTION +function! s:VimLParser.check_missing_endfunction(ends, pos) abort + if self.context[0].type ==# s:NODE_FUNCTION throw s:Err(printf('E126: Missing :endfunction: %s', a:ends), a:pos) endif endfunction -function! s:VimLParser.check_missing_endif(ends, pos) - if self.context[0].type == s:NODE_IF || self.context[0].type == s:NODE_ELSEIF || self.context[0].type == s:NODE_ELSE +function! s:VimLParser.check_missing_endif(ends, pos) abort + if self.context[0].type ==# s:NODE_IF || self.context[0].type ==# s:NODE_ELSEIF || self.context[0].type ==# s:NODE_ELSE throw s:Err(printf('E171: Missing :endif: %s', a:ends), a:pos) endif endfunction -function! s:VimLParser.check_missing_endtry(ends, pos) - if self.context[0].type == s:NODE_TRY || self.context[0].type == s:NODE_CATCH || self.context[0].type == s:NODE_FINALLY +function! s:VimLParser.check_missing_endtry(ends, pos) abort + if self.context[0].type ==# s:NODE_TRY || self.context[0].type ==# s:NODE_CATCH || self.context[0].type ==# s:NODE_FINALLY throw s:Err(printf('E600: Missing :endtry: %s', a:ends), a:pos) endif endfunction -function! s:VimLParser.check_missing_endwhile(ends, pos) - if self.context[0].type == s:NODE_WHILE +function! s:VimLParser.check_missing_endwhile(ends, pos) abort + if self.context[0].type ==# s:NODE_WHILE throw s:Err(printf('E170: Missing :endwhile: %s', a:ends), a:pos) endif endfunction -function! s:VimLParser.check_missing_endfor(ends, pos) - if self.context[0].type == s:NODE_FOR +function! s:VimLParser.check_missing_endfor(ends, pos) abort + if self.context[0].type ==# s:NODE_FOR throw s:Err(printf('E170: Missing :endfor: %s', a:ends), a:pos) endif endfunction -function! s:VimLParser.parse(reader) +function! s:VimLParser.parse(reader) abort let self.reader = a:reader let self.context = [] let toplevel = s:Node(s:NODE_TOPLEVEL) + let toplevel.pos = self.reader.getpos() let toplevel.body = [] call self.push_context(toplevel) while self.reader.peek() !=# '' @@ -502,7 +518,7 @@ function! s:VimLParser.parse(reader) return toplevel endfunction -function! s:VimLParser.parse_one_cmd() +function! s:VimLParser.parse_one_cmd() abort let self.ea = s:ExArg() if self.reader.peekn(2) ==# '#!' @@ -528,7 +544,7 @@ function! s:VimLParser.parse_one_cmd() endfunction " FIXME: -function! s:VimLParser.parse_command_modifiers() +function! s:VimLParser.parse_command_modifiers() abort let modifiers = [] while s:TRUE let pos = self.reader.tell() @@ -540,42 +556,42 @@ function! s:VimLParser.parse_command_modifiers() let k = self.reader.read_alpha() let c = self.reader.peekn(1) call self.reader.skip_white() - if stridx('aboveleft', k) == 0 && len(k) >= 3 " abo\%[veleft] + if stridx('aboveleft', k) ==# 0 && len(k) >= 3 " abo\%[veleft] call add(modifiers, {'name': 'aboveleft'}) - elseif stridx('belowright', k) == 0 && len(k) >= 3 " bel\%[owright] + elseif stridx('belowright', k) ==# 0 && len(k) >= 3 " bel\%[owright] call add(modifiers, {'name': 'belowright'}) - elseif stridx('browse', k) == 0 && len(k) >= 3 " bro\%[wse] + elseif stridx('browse', k) ==# 0 && len(k) >= 3 " bro\%[wse] call add(modifiers, {'name': 'browse'}) - elseif stridx('botright', k) == 0 && len(k) >= 2 " bo\%[tright] + elseif stridx('botright', k) ==# 0 && len(k) >= 2 " bo\%[tright] call add(modifiers, {'name': 'botright'}) - elseif stridx('confirm', k) == 0 && len(k) >= 4 " conf\%[irm] + elseif stridx('confirm', k) ==# 0 && len(k) >= 4 " conf\%[irm] call add(modifiers, {'name': 'confirm'}) - elseif stridx('keepmarks', k) == 0 && len(k) >= 3 " kee\%[pmarks] + elseif stridx('keepmarks', k) ==# 0 && len(k) >= 3 " kee\%[pmarks] call add(modifiers, {'name': 'keepmarks'}) - elseif stridx('keepalt', k) == 0 && len(k) >= 5 " keepa\%[lt] + elseif stridx('keepalt', k) ==# 0 && len(k) >= 5 " keepa\%[lt] call add(modifiers, {'name': 'keepalt'}) - elseif stridx('keepjumps', k) == 0 && len(k) >= 5 " keepj\%[umps] + elseif stridx('keepjumps', k) ==# 0 && len(k) >= 5 " keepj\%[umps] call add(modifiers, {'name': 'keepjumps'}) - elseif stridx('keeppatterns', k) == 0 && len(k) >= 5 " keepp\%[atterns] + elseif stridx('keeppatterns', k) ==# 0 && len(k) >= 5 " keepp\%[atterns] call add(modifiers, {'name': 'keeppatterns'}) - elseif stridx('hide', k) == 0 && len(k) >= 3 "hid\%[e] + elseif stridx('hide', k) ==# 0 && len(k) >= 3 " hid\%[e] if self.ends_excmds(c) break endif call add(modifiers, {'name': 'hide'}) - elseif stridx('lockmarks', k) == 0 && len(k) >= 3 " loc\%[kmarks] + elseif stridx('lockmarks', k) ==# 0 && len(k) >= 3 " loc\%[kmarks] call add(modifiers, {'name': 'lockmarks'}) - elseif stridx('leftabove', k) == 0 && len(k) >= 5 " lefta\%[bove] + elseif stridx('leftabove', k) ==# 0 && len(k) >= 5 " lefta\%[bove] call add(modifiers, {'name': 'leftabove'}) - elseif stridx('noautocmd', k) == 0 && len(k) >= 3 " noa\%[utocmd] + elseif stridx('noautocmd', k) ==# 0 && len(k) >= 3 " noa\%[utocmd] call add(modifiers, {'name': 'noautocmd'}) - elseif stridx('noswapfile', k) == 0 && len(k) >= 3 " :nos\%[wapfile] + elseif stridx('noswapfile', k) ==# 0 && len(k) >= 3 " :nos\%[wapfile] call add(modifiers, {'name': 'noswapfile'}) - elseif stridx('rightbelow', k) == 0 && len(k) >= 6 "rightb\%[elow] + elseif stridx('rightbelow', k) ==# 0 && len(k) >= 6 " rightb\%[elow] call add(modifiers, {'name': 'rightbelow'}) - elseif stridx('sandbox', k) == 0 && len(k) >= 3 " san\%[dbox] + elseif stridx('sandbox', k) ==# 0 && len(k) >= 3 " san\%[dbox] call add(modifiers, {'name': 'sandbox'}) - elseif stridx('silent', k) == 0 && len(k) >= 3 " sil\%[ent] + elseif stridx('silent', k) ==# 0 && len(k) >= 3 " sil\%[ent] if c ==# '!' call self.reader.get() call add(modifiers, {'name': 'silent', 'bang': 1}) @@ -588,13 +604,13 @@ function! s:VimLParser.parse_command_modifiers() else call add(modifiers, {'name': 'tab'}) endif - elseif stridx('topleft', k) == 0 && len(k) >= 2 " to\%[pleft] + elseif stridx('topleft', k) ==# 0 && len(k) >= 2 " to\%[pleft] call add(modifiers, {'name': 'topleft'}) - elseif stridx('unsilent', k) == 0 && len(k) >= 3 " uns\%[ilent] + elseif stridx('unsilent', k) ==# 0 && len(k) >= 3 " uns\%[ilent] call add(modifiers, {'name': 'unsilent'}) - elseif stridx('vertical', k) == 0 && len(k) >= 4 " vert\%[ical] + elseif stridx('vertical', k) ==# 0 && len(k) >= 4 " vert\%[ical] call add(modifiers, {'name': 'vertical'}) - elseif stridx('verbose', k) == 0 && len(k) >= 4 " verb\%[ose] + elseif stridx('verbose', k) ==# 0 && len(k) >= 4 " verb\%[ose] if d !=# '' call add(modifiers, {'name': 'verbose', 'count': str2nr(d, 10)}) else @@ -609,7 +625,7 @@ function! s:VimLParser.parse_command_modifiers() endfunction " FIXME: -function! s:VimLParser.parse_range() +function! s:VimLParser.parse_range() abort let tokens = [] while s:TRUE @@ -691,7 +707,7 @@ function! s:VimLParser.parse_range() endfunction " FIXME: -function! s:VimLParser.parse_pattern(delimiter) +function! s:VimLParser.parse_pattern(delimiter) abort let pattern = '' let endc = '' let inbracket = 0 @@ -700,7 +716,7 @@ function! s:VimLParser.parse_pattern(delimiter) if c ==# '' break endif - if c ==# a:delimiter && inbracket == 0 + if c ==# a:delimiter && inbracket ==# 0 let endc = c break endif @@ -721,7 +737,7 @@ function! s:VimLParser.parse_pattern(delimiter) return [pattern, endc] endfunction -function! s:VimLParser.parse_command() +function! s:VimLParser.parse_command() abort call self.reader.skip_white_and_colon() let self.ea.cmdpos = self.reader.getpos() @@ -735,7 +751,7 @@ function! s:VimLParser.parse_command() let self.ea.cmd = self.find_command() - if self.ea.cmd is s:NIL + if self.ea.cmd is# s:NIL call self.reader.setpos(self.ea.cmdpos) throw s:Err(printf('E492: Not an editor command: %s', self.reader.peekline()), self.ea.cmdpos) endif @@ -801,105 +817,110 @@ function! s:VimLParser.parse_command() call self._parse_command(self.ea.cmd.parser) endfunction +" TODO: self[a:parser] function! s:VimLParser._parse_command(parser) abort - if a:parser == 'parse_cmd_append' + if a:parser ==# 'parse_cmd_append' call self.parse_cmd_append() - elseif a:parser == 'parse_cmd_break' + elseif a:parser ==# 'parse_cmd_break' call self.parse_cmd_break() - elseif a:parser == 'parse_cmd_call' + elseif a:parser ==# 'parse_cmd_call' call self.parse_cmd_call() - elseif a:parser == 'parse_cmd_catch' + elseif a:parser ==# 'parse_cmd_catch' call self.parse_cmd_catch() - elseif a:parser == 'parse_cmd_common' + elseif a:parser ==# 'parse_cmd_common' call self.parse_cmd_common() - elseif a:parser == 'parse_cmd_continue' + elseif a:parser ==# 'parse_cmd_continue' call self.parse_cmd_continue() - elseif a:parser == 'parse_cmd_delfunction' + elseif a:parser ==# 'parse_cmd_delfunction' call self.parse_cmd_delfunction() - elseif a:parser == 'parse_cmd_echo' + elseif a:parser ==# 'parse_cmd_echo' call self.parse_cmd_echo() - elseif a:parser == 'parse_cmd_echoerr' + elseif a:parser ==# 'parse_cmd_echoerr' call self.parse_cmd_echoerr() - elseif a:parser == 'parse_cmd_echohl' + elseif a:parser ==# 'parse_cmd_echohl' call self.parse_cmd_echohl() - elseif a:parser == 'parse_cmd_echomsg' + elseif a:parser ==# 'parse_cmd_echomsg' call self.parse_cmd_echomsg() - elseif a:parser == 'parse_cmd_echon' + elseif a:parser ==# 'parse_cmd_echon' call self.parse_cmd_echon() - elseif a:parser == 'parse_cmd_else' + elseif a:parser ==# 'parse_cmd_else' call self.parse_cmd_else() - elseif a:parser == 'parse_cmd_elseif' + elseif a:parser ==# 'parse_cmd_elseif' call self.parse_cmd_elseif() - elseif a:parser == 'parse_cmd_endfor' + elseif a:parser ==# 'parse_cmd_endfor' call self.parse_cmd_endfor() - elseif a:parser == 'parse_cmd_endfunction' + elseif a:parser ==# 'parse_cmd_endfunction' call self.parse_cmd_endfunction() - elseif a:parser == 'parse_cmd_endif' + elseif a:parser ==# 'parse_cmd_endif' call self.parse_cmd_endif() - elseif a:parser == 'parse_cmd_endtry' + elseif a:parser ==# 'parse_cmd_endtry' call self.parse_cmd_endtry() - elseif a:parser == 'parse_cmd_endwhile' + elseif a:parser ==# 'parse_cmd_endwhile' call self.parse_cmd_endwhile() - elseif a:parser == 'parse_cmd_execute' + elseif a:parser ==# 'parse_cmd_execute' call self.parse_cmd_execute() - elseif a:parser == 'parse_cmd_finally' + elseif a:parser ==# 'parse_cmd_finally' call self.parse_cmd_finally() - elseif a:parser == 'parse_cmd_finish' + elseif a:parser ==# 'parse_cmd_finish' call self.parse_cmd_finish() - elseif a:parser == 'parse_cmd_for' + elseif a:parser ==# 'parse_cmd_for' call self.parse_cmd_for() - elseif a:parser == 'parse_cmd_function' + elseif a:parser ==# 'parse_cmd_function' call self.parse_cmd_function() - elseif a:parser == 'parse_cmd_if' + elseif a:parser ==# 'parse_cmd_if' call self.parse_cmd_if() - elseif a:parser == 'parse_cmd_insert' + elseif a:parser ==# 'parse_cmd_insert' call self.parse_cmd_insert() - elseif a:parser == 'parse_cmd_let' + elseif a:parser ==# 'parse_cmd_let' call self.parse_cmd_let() - elseif a:parser == 'parse_cmd_loadkeymap' + elseif a:parser ==# 'parse_cmd_const' + call self.parse_cmd_const() + elseif a:parser ==# 'parse_cmd_loadkeymap' call self.parse_cmd_loadkeymap() - elseif a:parser == 'parse_cmd_lockvar' + elseif a:parser ==# 'parse_cmd_lockvar' call self.parse_cmd_lockvar() - elseif a:parser == 'parse_cmd_lua' + elseif a:parser ==# 'parse_cmd_lua' call self.parse_cmd_lua() - elseif a:parser == 'parse_cmd_modifier_range' + elseif a:parser ==# 'parse_cmd_modifier_range' call self.parse_cmd_modifier_range() - elseif a:parser == 'parse_cmd_mzscheme' + elseif a:parser ==# 'parse_cmd_mzscheme' call self.parse_cmd_mzscheme() - elseif a:parser == 'parse_cmd_perl' + elseif a:parser ==# 'parse_cmd_perl' call self.parse_cmd_perl() - elseif a:parser == 'parse_cmd_python' + elseif a:parser ==# 'parse_cmd_python' call self.parse_cmd_python() - elseif a:parser == 'parse_cmd_python3' + elseif a:parser ==# 'parse_cmd_python3' call self.parse_cmd_python3() - elseif a:parser == 'parse_cmd_return' + elseif a:parser ==# 'parse_cmd_return' call self.parse_cmd_return() - elseif a:parser == 'parse_cmd_ruby' + elseif a:parser ==# 'parse_cmd_ruby' call self.parse_cmd_ruby() - elseif a:parser == 'parse_cmd_tcl' + elseif a:parser ==# 'parse_cmd_tcl' call self.parse_cmd_tcl() - elseif a:parser == 'parse_cmd_throw' + elseif a:parser ==# 'parse_cmd_throw' call self.parse_cmd_throw() - elseif a:parser == 'parse_cmd_try' + elseif a:parser ==# 'parse_cmd_eval' + call self.parse_cmd_eval() + elseif a:parser ==# 'parse_cmd_try' call self.parse_cmd_try() - elseif a:parser == 'parse_cmd_unlet' + elseif a:parser ==# 'parse_cmd_unlet' call self.parse_cmd_unlet() - elseif a:parser == 'parse_cmd_unlockvar' + elseif a:parser ==# 'parse_cmd_unlockvar' call self.parse_cmd_unlockvar() - elseif a:parser == 'parse_cmd_usercmd' + elseif a:parser ==# 'parse_cmd_usercmd' call self.parse_cmd_usercmd() - elseif a:parser == 'parse_cmd_while' + elseif a:parser ==# 'parse_cmd_while' call self.parse_cmd_while() - elseif a:parser == 'parse_wincmd' + elseif a:parser ==# 'parse_wincmd' call self.parse_wincmd() - elseif a:parser == 'parse_cmd_syntax' + elseif a:parser ==# 'parse_cmd_syntax' call self.parse_cmd_syntax() else throw printf('unknown parser: %s', string(a:parser)) endif endfunction -function! s:VimLParser.find_command() +function! s:VimLParser.find_command() abort let c = self.reader.peekn(1) let name = '' @@ -923,7 +944,7 @@ function! s:VimLParser.find_command() endif endif - if name == '' + if name ==# '' return s:NIL endif @@ -934,16 +955,16 @@ function! s:VimLParser.find_command() let cmd = s:NIL for x in self.builtin_commands - if stridx(x.name, name) == 0 && len(name) >= x.minlen + if stridx(x.name, name) ==# 0 && len(name) >= x.minlen unlet cmd let cmd = x break endif endfor - if self.neovim + if self.neovim for x in self.neovim_additional_commands - if stridx(x.name, name) == 0 && len(name) >= x.minlen + if stridx(x.name, name) ==# 0 && len(name) >= x.minlen unlet cmd let cmd = x break @@ -951,16 +972,16 @@ function! s:VimLParser.find_command() endfor for x in self.neovim_removed_commands - if stridx(x.name, name) == 0 && len(name) >= x.minlen + if stridx(x.name, name) ==# 0 && len(name) >= x.minlen unlet cmd let cmd = s:NIL break endif endfor endif - + " FIXME: user defined command - if (cmd is s:NIL || cmd.name ==# 'Print') && name =~# '^[A-Z]' + if (cmd is# s:NIL || cmd.name ==# 'Print') && name =~# '^[A-Z]' let name .= self.reader.read_alnum() unlet cmd let cmd = {'name': name, 'flags': 'USERCMD', 'parser': 'parse_cmd_usercmd'} @@ -972,13 +993,13 @@ function! s:VimLParser.find_command() endfunction " TODO: -function! s:VimLParser.parse_hashbang() +function! s:VimLParser.parse_hashbang() abort call self.reader.getn(-1) endfunction " TODO: " ++opt=val -function! s:VimLParser.parse_argopt() +function! s:VimLParser.parse_argopt() abort while self.reader.p(0) ==# '+' && self.reader.p(1) ==# '+' let s = self.reader.peekn(20) if s =~# '^++bin\>' @@ -1022,7 +1043,7 @@ endfunction " TODO: " +command -function! s:VimLParser.parse_argcmd() +function! s:VimLParser.parse_argcmd() abort if self.reader.peekn(1) ==# '+' call self.reader.getn(1) if self.reader.peekn(1) ==# ' ' @@ -1033,7 +1054,7 @@ function! s:VimLParser.parse_argcmd() endif endfunction -function! s:VimLParser.read_cmdarg() +function! s:VimLParser.read_cmdarg() abort let r = '' while s:TRUE let c = self.reader.peekn(1) @@ -1049,7 +1070,7 @@ function! s:VimLParser.read_cmdarg() return r endfunction -function! s:VimLParser.parse_comment() +function! s:VimLParser.parse_comment() abort let npos = self.reader.getpos() let c = self.reader.get() if c !=# '"' @@ -1061,7 +1082,7 @@ function! s:VimLParser.parse_comment() call self.add_node(node) endfunction -function! s:VimLParser.parse_trail() +function! s:VimLParser.parse_trail() abort call self.reader.skip_white() let c = self.reader.peek() if c ==# '' @@ -1079,7 +1100,7 @@ function! s:VimLParser.parse_trail() endfunction " modifier or range only command line -function! s:VimLParser.parse_cmd_modifier_range() +function! s:VimLParser.parse_cmd_modifier_range() abort let node = s:Node(s:NODE_EXCMD) let node.pos = self.ea.cmdpos let node.ea = self.ea @@ -1088,7 +1109,7 @@ function! s:VimLParser.parse_cmd_modifier_range() endfunction " TODO: -function! s:VimLParser.parse_cmd_common() +function! s:VimLParser.parse_cmd_common() abort let end = self.reader.getpos() if self.ea.cmd.flags =~# '\' && !self.ea.usefilter let end = self.separate_nextcmd() @@ -1114,7 +1135,7 @@ function! s:VimLParser.parse_cmd_common() call self.add_node(node) endfunction -function! s:VimLParser.separate_nextcmd() +function! s:VimLParser.separate_nextcmd() abort if self.ea.cmd.name ==# 'vimgrep' || self.ea.cmd.name ==# 'vimgrepadd' || self.ea.cmd.name ==# 'lvimgrep' || self.ea.cmd.name ==# 'lvimgrepadd' call self.skip_vimgrep_pat() endif @@ -1151,7 +1172,7 @@ function! s:VimLParser.separate_nextcmd() \ && ((self.ea.cmd.name !=# '@' && self.ea.cmd.name !=# '*') \ || self.reader.getpos() !=# self.ea.argpos) \ && (self.ea.cmd.name !=# 'redir' - \ || self.reader.getpos().i != self.ea.argpos.i + 1 || pc !=# '@')) + \ || self.reader.getpos().i !=# self.ea.argpos.i + 1 || pc !=# '@')) let has_cpo_bar = s:FALSE " &cpoptions =~ 'b' if (!has_cpo_bar || self.ea.cmd.flags !~# '\') && pc ==# '\' call self.reader.get() @@ -1170,7 +1191,7 @@ function! s:VimLParser.separate_nextcmd() endfunction " FIXME -function! s:VimLParser.skip_vimgrep_pat() +function! s:VimLParser.skip_vimgrep_pat() abort if self.reader.peekn(1) ==# '' " pass elseif s:isidc(self.reader.peekn(1)) @@ -1189,7 +1210,7 @@ function! s:VimLParser.skip_vimgrep_pat() endif endfunction -function! s:VimLParser.parse_cmd_append() +function! s:VimLParser.parse_cmd_append() abort call self.reader.setpos(self.ea.linepos) let cmdline = self.reader.readline() let lines = [cmdline] @@ -1212,11 +1233,11 @@ function! s:VimLParser.parse_cmd_append() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_insert() +function! s:VimLParser.parse_cmd_insert() abort call self.parse_cmd_append() endfunction -function! s:VimLParser.parse_cmd_loadkeymap() +function! s:VimLParser.parse_cmd_loadkeymap() abort call self.reader.setpos(self.ea.linepos) let cmdline = self.reader.readline() let lines = [cmdline] @@ -1234,7 +1255,7 @@ function! s:VimLParser.parse_cmd_loadkeymap() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_lua() +function! s:VimLParser.parse_cmd_lua() abort let lines = [] call self.reader.skip_white() if self.reader.peekn(2) ==# '<<' @@ -1271,43 +1292,43 @@ function! s:VimLParser.parse_cmd_lua() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_mzscheme() +function! s:VimLParser.parse_cmd_mzscheme() abort call self.parse_cmd_lua() endfunction -function! s:VimLParser.parse_cmd_perl() +function! s:VimLParser.parse_cmd_perl() abort call self.parse_cmd_lua() endfunction -function! s:VimLParser.parse_cmd_python() +function! s:VimLParser.parse_cmd_python() abort call self.parse_cmd_lua() endfunction -function! s:VimLParser.parse_cmd_python3() +function! s:VimLParser.parse_cmd_python3() abort call self.parse_cmd_lua() endfunction -function! s:VimLParser.parse_cmd_ruby() +function! s:VimLParser.parse_cmd_ruby() abort call self.parse_cmd_lua() endfunction -function! s:VimLParser.parse_cmd_tcl() +function! s:VimLParser.parse_cmd_tcl() abort call self.parse_cmd_lua() endfunction -function! s:VimLParser.parse_cmd_finish() +function! s:VimLParser.parse_cmd_finish() abort call self.parse_cmd_common() - if self.context[0].type == s:NODE_TOPLEVEL + if self.context[0].type ==# s:NODE_TOPLEVEL call self.reader.seek_end(0) endif endfunction " FIXME -function! s:VimLParser.parse_cmd_usercmd() +function! s:VimLParser.parse_cmd_usercmd() abort call self.parse_cmd_common() endfunction -function! s:VimLParser.parse_cmd_function() +function! s:VimLParser.parse_cmd_function() abort let pos = self.reader.tell() call self.reader.skip_white() @@ -1328,10 +1349,10 @@ function! s:VimLParser.parse_cmd_function() let left = self.parse_lvalue_func() call self.reader.skip_white() - if left.type == s:NODE_IDENTIFIER + if left.type ==# s:NODE_IDENTIFIER let s = left.value let ss = split(s, '\zs') - if ss[0] !=# '<' && !s:isupper(ss[0]) && stridx(s, ':') == -1 && stridx(s, '#') == -1 + if ss[0] !=# '<' && ss[0] !=# '_' && !s:isupper(ss[0]) && stridx(s, ':') ==# -1 && stridx(s, '#') ==# -1 throw s:Err(printf('E128: Function name must start with a capital or contain a colon: %s', s), left.pos) endif endif @@ -1350,18 +1371,19 @@ function! s:VimLParser.parse_cmd_function() let node.ea = self.ea let node.left = left let node.rlist = [] + let node.default_args = [] let node.attr = {'range': 0, 'abort': 0, 'dict': 0, 'closure': 0} let node.endfunction = s:NIL call self.reader.getn(1) let tokenizer = s:ExprTokenizer.new(self.reader) - if tokenizer.peek().type == s:TOKEN_PCLOSE + if tokenizer.peek().type ==# s:TOKEN_PCLOSE call tokenizer.get() else let named = {} while s:TRUE let varnode = s:Node(s:NODE_IDENTIFIER) let token = tokenizer.get() - if token.type == s:TOKEN_IDENTIFIER + if token.type ==# s:TOKEN_IDENTIFIER if !s:isargname(token.value) || token.value ==# 'firstline' || token.value ==# 'lastline' throw s:Err(printf('E125: Illegal argument: %s', token.value), token.pos) elseif has_key(named, token.value) @@ -1371,28 +1393,34 @@ function! s:VimLParser.parse_cmd_function() let varnode.pos = token.pos let varnode.value = token.value call add(node.rlist, varnode) + if tokenizer.peek().type ==# s:TOKEN_EQ + call tokenizer.get() + call add(node.default_args, self.parse_expr()) + elseif len(node.default_args) > 0 + throw s:Err('E989: Non-default argument follows default argument', varnode.pos) + endif " XXX: Vim doesn't skip white space before comma. F(a ,b) => E475 - if s:iswhite(self.reader.p(0)) && tokenizer.peek().type == s:TOKEN_COMMA + if s:iswhite(self.reader.p(0)) && tokenizer.peek().type ==# s:TOKEN_COMMA throw s:Err('E475: Invalid argument: White space is not allowed before comma', self.reader.getpos()) endif let token = tokenizer.get() - if token.type == s:TOKEN_COMMA + if token.type ==# s:TOKEN_COMMA " XXX: Vim allows last comma. F(a, b, ) => OK - if tokenizer.peek().type == s:TOKEN_PCLOSE + if tokenizer.peek().type ==# s:TOKEN_PCLOSE call tokenizer.get() break endif - elseif token.type == s:TOKEN_PCLOSE + elseif token.type ==# s:TOKEN_PCLOSE break else throw s:Err(printf('unexpected token: %s', token.value), token.pos) endif - elseif token.type == s:TOKEN_DOTDOTDOT + elseif token.type ==# s:TOKEN_DOTDOTDOT let varnode.pos = token.pos let varnode.value = token.value call add(node.rlist, varnode) let token = tokenizer.get() - if token.type == s:TOKEN_PCLOSE + if token.type ==# s:TOKEN_PCLOSE break else throw s:Err(printf('unexpected token: %s', token.value), token.pos) @@ -1424,12 +1452,12 @@ function! s:VimLParser.parse_cmd_function() call self.push_context(node) endfunction -function! s:VimLParser.parse_cmd_endfunction() +function! s:VimLParser.parse_cmd_endfunction() abort call self.check_missing_endif('ENDFUNCTION', self.ea.cmdpos) call self.check_missing_endtry('ENDFUNCTION', self.ea.cmdpos) call self.check_missing_endwhile('ENDFUNCTION', self.ea.cmdpos) call self.check_missing_endfor('ENDFUNCTION', self.ea.cmdpos) - if self.context[0].type != s:NODE_FUNCTION + if self.context[0].type !=# s:NODE_FUNCTION throw s:Err('E193: :endfunction not inside a function', self.ea.cmdpos) endif call self.reader.getn(-1) @@ -1440,7 +1468,7 @@ function! s:VimLParser.parse_cmd_endfunction() call self.pop_context() endfunction -function! s:VimLParser.parse_cmd_delfunction() +function! s:VimLParser.parse_cmd_delfunction() abort let node = s:Node(s:NODE_DELFUNCTION) let node.pos = self.ea.cmdpos let node.ea = self.ea @@ -1448,8 +1476,8 @@ function! s:VimLParser.parse_cmd_delfunction() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_return() - if self.find_context(s:NODE_FUNCTION) == -1 +function! s:VimLParser.parse_cmd_return() abort + if self.find_context(s:NODE_FUNCTION) ==# -1 throw s:Err('E133: :return not inside a function', self.ea.cmdpos) endif let node = s:Node(s:NODE_RETURN) @@ -1464,7 +1492,7 @@ function! s:VimLParser.parse_cmd_return() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_call() +function! s:VimLParser.parse_cmd_call() abort let node = s:Node(s:NODE_EXCALL) let node.pos = self.ea.cmdpos let node.ea = self.ea @@ -1474,13 +1502,59 @@ function! s:VimLParser.parse_cmd_call() throw s:Err('E471: Argument required', self.reader.getpos()) endif let node.left = self.parse_expr() - if node.left.type != s:NODE_CALL + if node.left.type !=# s:NODE_CALL throw s:Err('Not an function call', node.left.pos) endif call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_let() +function! s:VimLParser.parse_heredoc() abort + let node = s:Node(s:NODE_HEREDOC) + let node.pos = self.ea.cmdpos + let node.op = '' + let node.rlist = [] + let node.body = [] + + while s:TRUE + call self.reader.skip_white() + let pos = self.reader.getpos() + let key = self.reader.read_word() + if key ==# '' + break + endif + if !s:islower(key[0]) + let node.op = key + break + else + let keynode = s:Node(s:NODE_STRING) + let keynode.pos = pos + let keynode.value = key + call add(node.rlist, keynode) + endif + endwhile + if node.op ==# '' + throw s:Err('E172: Missing marker', self.reader.getpos()) + endif + call self.parse_trail() + while s:TRUE + if self.reader.peek() ==# '' + break + endif + let pos = self.reader.getpos() + let line = self.reader.getn(-1) + if line ==# node.op + return node + endif + let linenode = s:Node(s:NODE_STRING) + let linenode.pos = pos + let linenode.value = line + call add(node.body, linenode) + call self.reader.get() + endwhile + throw s:Err(printf("E990: Missing end marker '%s'", node.op), self.reader.getpos()) +endfunction + +function! s:VimLParser.parse_cmd_let() abort let pos = self.reader.tell() call self.reader.skip_white() @@ -1495,9 +1569,15 @@ function! s:VimLParser.parse_cmd_let() call self.reader.skip_white() let s1 = self.reader.peekn(1) let s2 = self.reader.peekn(2) + " TODO check scriptversion? + if s2 ==# '..' + let s2 = self.reader.peekn(3) + elseif s2 ==# '=<' + let s2 = self.reader.peekn(3) + endif " :let {var-name} .. - if self.ends_excmds(s1) || (s2 !=# '+=' && s2 !=# '-=' && s2 !=# '.=' && s1 !=# '=') + if self.ends_excmds(s1) || (s2 !=# '+=' && s2 !=# '-=' && s2 !=# '.=' && s2 !=# '..=' && s2 !=# '*=' && s2 !=# '/=' && s2 !=# '%=' && s2 !=# '=<<' && s1 !=# '=') call self.reader.seek_set(pos) call self.parse_cmd_common() return @@ -1512,9 +1592,16 @@ function! s:VimLParser.parse_cmd_let() let node.list = lhs.list let node.rest = lhs.rest let node.right = s:NIL - if s2 ==# '+=' || s2 ==# '-=' || s2 ==# '.=' - call self.reader.getn(2) + if s2 ==# '+=' || s2 ==# '-=' || s2 ==# '.=' || s2 ==# '..=' || s2 ==# '*=' || s2 ==# '/=' || s2 ==# '%=' + call self.reader.getn(len(s2)) let node.op = s2 + elseif s2 ==# '=<<' + call self.reader.getn(len(s2)) + call self.reader.skip_white() + let node.op = s2 + let node.right = self.parse_heredoc() + call self.add_node(node) + return elseif s1 ==# '=' call self.reader.getn(1) let node.op = s1 @@ -1525,7 +1612,42 @@ function! s:VimLParser.parse_cmd_let() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_unlet() +function! s:VimLParser.parse_cmd_const() abort + let pos = self.reader.tell() + call self.reader.skip_white() + + " :const + if self.ends_excmds(self.reader.peek()) + call self.reader.seek_set(pos) + call self.parse_cmd_common() + return + endif + + let lhs = self.parse_constlhs() + call self.reader.skip_white() + let s1 = self.reader.peekn(1) + + " :const {var-name} + if self.ends_excmds(s1) || s1 !=# '=' + call self.reader.seek_set(pos) + call self.parse_cmd_common() + return + endif + + " :const left op right + let node = s:Node(s:NODE_CONST) + let node.pos = self.ea.cmdpos + let node.ea = self.ea + call self.reader.getn(1) + let node.op = s1 + let node.left = lhs.left + let node.list = lhs.list + let node.rest = lhs.rest + let node.right = self.parse_expr() + call self.add_node(node) +endfunction + +function! s:VimLParser.parse_cmd_unlet() abort let node = s:Node(s:NODE_UNLET) let node.pos = self.ea.cmdpos let node.ea = self.ea @@ -1533,7 +1655,7 @@ function! s:VimLParser.parse_cmd_unlet() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_lockvar() +function! s:VimLParser.parse_cmd_lockvar() abort let node = s:Node(s:NODE_LOCKVAR) let node.pos = self.ea.cmdpos let node.ea = self.ea @@ -1547,7 +1669,7 @@ function! s:VimLParser.parse_cmd_lockvar() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_unlockvar() +function! s:VimLParser.parse_cmd_unlockvar() abort let node = s:Node(s:NODE_UNLOCKVAR) let node.pos = self.ea.cmdpos let node.ea = self.ea @@ -1561,7 +1683,7 @@ function! s:VimLParser.parse_cmd_unlockvar() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_if() +function! s:VimLParser.parse_cmd_if() abort let node = s:Node(s:NODE_IF) let node.pos = self.ea.cmdpos let node.body = [] @@ -1574,11 +1696,11 @@ function! s:VimLParser.parse_cmd_if() call self.push_context(node) endfunction -function! s:VimLParser.parse_cmd_elseif() - if self.context[0].type != s:NODE_IF && self.context[0].type != s:NODE_ELSEIF +function! s:VimLParser.parse_cmd_elseif() abort + if self.context[0].type !=# s:NODE_IF && self.context[0].type !=# s:NODE_ELSEIF throw s:Err('E582: :elseif without :if', self.ea.cmdpos) endif - if self.context[0].type != s:NODE_IF + if self.context[0].type !=# s:NODE_IF call self.pop_context() endif let node = s:Node(s:NODE_ELSEIF) @@ -1590,11 +1712,11 @@ function! s:VimLParser.parse_cmd_elseif() call self.push_context(node) endfunction -function! s:VimLParser.parse_cmd_else() - if self.context[0].type != s:NODE_IF && self.context[0].type != s:NODE_ELSEIF +function! s:VimLParser.parse_cmd_else() abort + if self.context[0].type !=# s:NODE_IF && self.context[0].type !=# s:NODE_ELSEIF throw s:Err('E581: :else without :if', self.ea.cmdpos) endif - if self.context[0].type != s:NODE_IF + if self.context[0].type !=# s:NODE_IF call self.pop_context() endif let node = s:Node(s:NODE_ELSE) @@ -1605,11 +1727,11 @@ function! s:VimLParser.parse_cmd_else() call self.push_context(node) endfunction -function! s:VimLParser.parse_cmd_endif() - if self.context[0].type != s:NODE_IF && self.context[0].type != s:NODE_ELSEIF && self.context[0].type != s:NODE_ELSE +function! s:VimLParser.parse_cmd_endif() abort + if self.context[0].type !=# s:NODE_IF && self.context[0].type !=# s:NODE_ELSEIF && self.context[0].type !=# s:NODE_ELSE throw s:Err('E580: :endif without :if', self.ea.cmdpos) endif - if self.context[0].type != s:NODE_IF + if self.context[0].type !=# s:NODE_IF call self.pop_context() endif let node = s:Node(s:NODE_ENDIF) @@ -1619,7 +1741,7 @@ function! s:VimLParser.parse_cmd_endif() call self.pop_context() endfunction -function! s:VimLParser.parse_cmd_while() +function! s:VimLParser.parse_cmd_while() abort let node = s:Node(s:NODE_WHILE) let node.pos = self.ea.cmdpos let node.body = [] @@ -1630,8 +1752,8 @@ function! s:VimLParser.parse_cmd_while() call self.push_context(node) endfunction -function! s:VimLParser.parse_cmd_endwhile() - if self.context[0].type != s:NODE_WHILE +function! s:VimLParser.parse_cmd_endwhile() abort + if self.context[0].type !=# s:NODE_WHILE throw s:Err('E588: :endwhile without :while', self.ea.cmdpos) endif let node = s:Node(s:NODE_ENDWHILE) @@ -1641,7 +1763,7 @@ function! s:VimLParser.parse_cmd_endwhile() call self.pop_context() endfunction -function! s:VimLParser.parse_cmd_for() +function! s:VimLParser.parse_cmd_for() abort let node = s:Node(s:NODE_FOR) let node.pos = self.ea.cmdpos let node.body = [] @@ -1663,8 +1785,8 @@ function! s:VimLParser.parse_cmd_for() call self.push_context(node) endfunction -function! s:VimLParser.parse_cmd_endfor() - if self.context[0].type != s:NODE_FOR +function! s:VimLParser.parse_cmd_endfor() abort + if self.context[0].type !=# s:NODE_FOR throw s:Err('E588: :endfor without :for', self.ea.cmdpos) endif let node = s:Node(s:NODE_ENDFOR) @@ -1674,8 +1796,8 @@ function! s:VimLParser.parse_cmd_endfor() call self.pop_context() endfunction -function! s:VimLParser.parse_cmd_continue() - if self.find_context(s:NODE_WHILE) == -1 && self.find_context(s:NODE_FOR) == -1 +function! s:VimLParser.parse_cmd_continue() abort + if self.find_context(s:NODE_WHILE) ==# -1 && self.find_context(s:NODE_FOR) ==# -1 throw s:Err('E586: :continue without :while or :for', self.ea.cmdpos) endif let node = s:Node(s:NODE_CONTINUE) @@ -1684,8 +1806,8 @@ function! s:VimLParser.parse_cmd_continue() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_break() - if self.find_context(s:NODE_WHILE) == -1 && self.find_context(s:NODE_FOR) == -1 +function! s:VimLParser.parse_cmd_break() abort + if self.find_context(s:NODE_WHILE) ==# -1 && self.find_context(s:NODE_FOR) ==# -1 throw s:Err('E587: :break without :while or :for', self.ea.cmdpos) endif let node = s:Node(s:NODE_BREAK) @@ -1694,7 +1816,7 @@ function! s:VimLParser.parse_cmd_break() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_try() +function! s:VimLParser.parse_cmd_try() abort let node = s:Node(s:NODE_TRY) let node.pos = self.ea.cmdpos let node.body = [] @@ -1706,13 +1828,13 @@ function! s:VimLParser.parse_cmd_try() call self.push_context(node) endfunction -function! s:VimLParser.parse_cmd_catch() - if self.context[0].type == s:NODE_FINALLY +function! s:VimLParser.parse_cmd_catch() abort + if self.context[0].type ==# s:NODE_FINALLY throw s:Err('E604: :catch after :finally', self.ea.cmdpos) - elseif self.context[0].type != s:NODE_TRY && self.context[0].type != s:NODE_CATCH + elseif self.context[0].type !=# s:NODE_TRY && self.context[0].type !=# s:NODE_CATCH throw s:Err('E603: :catch without :try', self.ea.cmdpos) endif - if self.context[0].type != s:NODE_TRY + if self.context[0].type !=# s:NODE_TRY call self.pop_context() endif let node = s:Node(s:NODE_CATCH) @@ -1728,11 +1850,11 @@ function! s:VimLParser.parse_cmd_catch() call self.push_context(node) endfunction -function! s:VimLParser.parse_cmd_finally() - if self.context[0].type != s:NODE_TRY && self.context[0].type != s:NODE_CATCH +function! s:VimLParser.parse_cmd_finally() abort + if self.context[0].type !=# s:NODE_TRY && self.context[0].type !=# s:NODE_CATCH throw s:Err('E606: :finally without :try', self.ea.cmdpos) endif - if self.context[0].type != s:NODE_TRY + if self.context[0].type !=# s:NODE_TRY call self.pop_context() endif let node = s:Node(s:NODE_FINALLY) @@ -1743,11 +1865,11 @@ function! s:VimLParser.parse_cmd_finally() call self.push_context(node) endfunction -function! s:VimLParser.parse_cmd_endtry() - if self.context[0].type != s:NODE_TRY && self.context[0].type != s:NODE_CATCH && self.context[0].type != s:NODE_FINALLY +function! s:VimLParser.parse_cmd_endtry() abort + if self.context[0].type !=# s:NODE_TRY && self.context[0].type !=# s:NODE_CATCH && self.context[0].type !=# s:NODE_FINALLY throw s:Err('E602: :endtry without :try', self.ea.cmdpos) endif - if self.context[0].type != s:NODE_TRY + if self.context[0].type !=# s:NODE_TRY call self.pop_context() endif let node = s:Node(s:NODE_ENDTRY) @@ -1757,7 +1879,7 @@ function! s:VimLParser.parse_cmd_endtry() call self.pop_context() endfunction -function! s:VimLParser.parse_cmd_throw() +function! s:VimLParser.parse_cmd_throw() abort let node = s:Node(s:NODE_THROW) let node.pos = self.ea.cmdpos let node.ea = self.ea @@ -1765,7 +1887,15 @@ function! s:VimLParser.parse_cmd_throw() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_echo() +function! s:VimLParser.parse_cmd_eval() abort + let node = s:Node(s:NODE_EVAL) + let node.pos = self.ea.cmdpos + let node.ea = self.ea + let node.left = self.parse_expr() + call self.add_node(node) +endfunction + +function! s:VimLParser.parse_cmd_echo() abort let node = s:Node(s:NODE_ECHO) let node.pos = self.ea.cmdpos let node.ea = self.ea @@ -1773,7 +1903,7 @@ function! s:VimLParser.parse_cmd_echo() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_echon() +function! s:VimLParser.parse_cmd_echon() abort let node = s:Node(s:NODE_ECHON) let node.pos = self.ea.cmdpos let node.ea = self.ea @@ -1781,7 +1911,7 @@ function! s:VimLParser.parse_cmd_echon() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_echohl() +function! s:VimLParser.parse_cmd_echohl() abort let node = s:Node(s:NODE_ECHOHL) let node.pos = self.ea.cmdpos let node.ea = self.ea @@ -1792,7 +1922,7 @@ function! s:VimLParser.parse_cmd_echohl() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_echomsg() +function! s:VimLParser.parse_cmd_echomsg() abort let node = s:Node(s:NODE_ECHOMSG) let node.pos = self.ea.cmdpos let node.ea = self.ea @@ -1800,7 +1930,7 @@ function! s:VimLParser.parse_cmd_echomsg() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_echoerr() +function! s:VimLParser.parse_cmd_echoerr() abort let node = s:Node(s:NODE_ECHOERR) let node.pos = self.ea.cmdpos let node.ea = self.ea @@ -1808,7 +1938,7 @@ function! s:VimLParser.parse_cmd_echoerr() call self.add_node(node) endfunction -function! s:VimLParser.parse_cmd_execute() +function! s:VimLParser.parse_cmd_execute() abort let node = s:Node(s:NODE_EXECUTE) let node.pos = self.ea.cmdpos let node.ea = self.ea @@ -1816,11 +1946,11 @@ function! s:VimLParser.parse_cmd_execute() call self.add_node(node) endfunction -function! s:VimLParser.parse_expr() +function! s:VimLParser.parse_expr() abort return s:ExprParser.new(self.reader).parse() endfunction -function! s:VimLParser.parse_exprlist() +function! s:VimLParser.parse_exprlist() abort let list = [] while s:TRUE call self.reader.skip_white() @@ -1834,31 +1964,55 @@ function! s:VimLParser.parse_exprlist() return list endfunction -function! s:VimLParser.parse_lvalue_func() +function! s:VimLParser.parse_lvalue_func() abort let p = s:LvalueParser.new(self.reader) let node = p.parse() - if node.type == s:NODE_IDENTIFIER || node.type == s:NODE_CURLYNAME || node.type == s:NODE_SUBSCRIPT || node.type == s:NODE_DOT || node.type == s:NODE_OPTION || node.type == s:NODE_ENV || node.type == s:NODE_REG + if node.type ==# s:NODE_IDENTIFIER || node.type ==# s:NODE_CURLYNAME || node.type ==# s:NODE_SUBSCRIPT || node.type ==# s:NODE_DOT || node.type ==# s:NODE_OPTION || node.type ==# s:NODE_ENV || node.type ==# s:NODE_REG return node endif throw s:Err('Invalid Expression', node.pos) endfunction " FIXME: -function! s:VimLParser.parse_lvalue() +function! s:VimLParser.parse_lvalue() abort + let p = s:LvalueParser.new(self.reader) + let node = p.parse() + if node.type ==# s:NODE_IDENTIFIER + if !s:isvarname(node.value) + throw s:Err(printf('E461: Illegal variable name: %s', node.value), node.pos) + endif + endif + if node.type ==# s:NODE_IDENTIFIER || node.type ==# s:NODE_CURLYNAME || node.type ==# s:NODE_SUBSCRIPT || node.type ==# s:NODE_SLICE || node.type ==# s:NODE_DOT || node.type ==# s:NODE_OPTION || node.type ==# s:NODE_ENV || node.type ==# s:NODE_REG + return node + endif + throw s:Err('Invalid Expression', node.pos) +endfunction + +" TODO: merge with s:VimLParser.parse_lvalue() +function! s:VimLParser.parse_constlvalue() abort let p = s:LvalueParser.new(self.reader) let node = p.parse() - if node.type == s:NODE_IDENTIFIER + if node.type ==# s:NODE_IDENTIFIER if !s:isvarname(node.value) throw s:Err(printf('E461: Illegal variable name: %s', node.value), node.pos) endif endif - if node.type == s:NODE_IDENTIFIER || node.type == s:NODE_CURLYNAME || node.type == s:NODE_SUBSCRIPT || node.type == s:NODE_SLICE || node.type == s:NODE_DOT || node.type == s:NODE_OPTION || node.type == s:NODE_ENV || node.type == s:NODE_REG + if node.type ==# s:NODE_IDENTIFIER || node.type ==# s:NODE_CURLYNAME return node + elseif node.type ==# s:NODE_SUBSCRIPT || node.type ==# s:NODE_SLICE || node.type ==# s:NODE_DOT + throw s:Err('E996: Cannot lock a list or dict', node.pos) + elseif node.type ==# s:NODE_OPTION + throw s:Err('E996: Cannot lock an option', node.pos) + elseif node.type ==# s:NODE_ENV + throw s:Err('E996: Cannot lock an environment variable', node.pos) + elseif node.type ==# s:NODE_REG + throw s:Err('E996: Cannot lock a register', node.pos) endif throw s:Err('Invalid Expression', node.pos) endfunction -function! s:VimLParser.parse_lvaluelist() + +function! s:VimLParser.parse_lvaluelist() abort let list = [] let node = self.parse_expr() call add(list, node) @@ -1874,25 +2028,25 @@ function! s:VimLParser.parse_lvaluelist() endfunction " FIXME: -function! s:VimLParser.parse_letlhs() +function! s:VimLParser.parse_letlhs() abort let lhs = {'left': s:NIL, 'list': s:NIL, 'rest': s:NIL} let tokenizer = s:ExprTokenizer.new(self.reader) - if tokenizer.peek().type == s:TOKEN_SQOPEN + if tokenizer.peek().type ==# s:TOKEN_SQOPEN call tokenizer.get() let lhs.list = [] while s:TRUE let node = self.parse_lvalue() call add(lhs.list, node) let token = tokenizer.get() - if token.type == s:TOKEN_SQCLOSE + if token.type ==# s:TOKEN_SQCLOSE break - elseif token.type == s:TOKEN_COMMA + elseif token.type ==# s:TOKEN_COMMA continue - elseif token.type == s:TOKEN_SEMICOLON + elseif token.type ==# s:TOKEN_SEMICOLON let node = self.parse_lvalue() let lhs.rest = node let token = tokenizer.get() - if token.type == s:TOKEN_SQCLOSE + if token.type ==# s:TOKEN_SQCLOSE break else throw s:Err(printf('E475 Invalid argument: %s', token.value), token.pos) @@ -1907,12 +2061,46 @@ function! s:VimLParser.parse_letlhs() return lhs endfunction -function! s:VimLParser.ends_excmds(c) +" TODO: merge with s:VimLParser.parse_letlhs() ? +function! s:VimLParser.parse_constlhs() abort + let lhs = {'left': s:NIL, 'list': s:NIL, 'rest': s:NIL} + let tokenizer = s:ExprTokenizer.new(self.reader) + if tokenizer.peek().type ==# s:TOKEN_SQOPEN + call tokenizer.get() + let lhs.list = [] + while s:TRUE + let node = self.parse_lvalue() + call add(lhs.list, node) + let token = tokenizer.get() + if token.type ==# s:TOKEN_SQCLOSE + break + elseif token.type ==# s:TOKEN_COMMA + continue + elseif token.type ==# s:TOKEN_SEMICOLON + let node = self.parse_lvalue() + let lhs.rest = node + let token = tokenizer.get() + if token.type ==# s:TOKEN_SQCLOSE + break + else + throw s:Err(printf('E475 Invalid argument: %s', token.value), token.pos) + endif + else + throw s:Err(printf('E475 Invalid argument: %s', token.value), token.pos) + endif + endwhile + else + let lhs.left = self.parse_constlvalue() + endif + return lhs +endfunction + +function! s:VimLParser.ends_excmds(c) abort return a:c ==# '' || a:c ==# '|' || a:c ==# '"' || a:c ==# '' || a:c ==# '' endfunction " FIXME: validate argument -function! s:VimLParser.parse_wincmd() +function! s:VimLParser.parse_wincmd() abort let c = self.reader.getn(1) if c ==# '' throw s:Err('E471: Argument required', self.reader.getpos()) @@ -1935,17 +2123,17 @@ function! s:VimLParser.parse_wincmd() endfunction " FIXME: validate argument -function! s:VimLParser.parse_cmd_syntax() +function! s:VimLParser.parse_cmd_syntax() abort let end = self.reader.getpos() while s:TRUE let end = self.reader.getpos() let c = self.reader.peek() - if c == "/" || c == "'" || c == "\"" + if c ==# '/' || c ==# "'" || c ==# '"' call self.reader.getn(1) call self.parse_pattern(c) - elseif c == "=" + elseif c ==# '=' call self.reader.getn(1) - call self.parse_pattern(" ") + call self.parse_pattern(' ') elseif self.ends_excmds(c) break endif @@ -2077,7 +2265,7 @@ let s:VimLParser.builtin_commands = [ \ {'name': 'debug', 'minlen': 3, 'flags': 'NEEDARG|EXTRA|NOTRLCOM|SBOXOK|CMDWIN', 'parser': 'parse_cmd_common'}, \ {'name': 'debuggreedy', 'minlen': 6, 'flags': 'RANGE|NOTADR|ZEROR|TRLBAR|CMDWIN', 'parser': 'parse_cmd_common'}, \ {'name': 'delcommand', 'minlen': 4, 'flags': 'NEEDARG|WORD1|TRLBAR|CMDWIN', 'parser': 'parse_cmd_common'}, - \ {'name': 'delfunction', 'minlen': 4, 'flags': 'NEEDARG|WORD1|CMDWIN', 'parser': 'parse_cmd_delfunction'}, + \ {'name': 'delfunction', 'minlen': 4, 'flags': 'BANG|NEEDARG|WORD1|CMDWIN', 'parser': 'parse_cmd_delfunction'}, \ {'name': 'diffupdate', 'minlen': 3, 'flags': 'BANG|TRLBAR', 'parser': 'parse_cmd_common'}, \ {'name': 'diffget', 'minlen': 5, 'flags': 'RANGE|EXTRA|TRLBAR|MODIFY', 'parser': 'parse_cmd_common'}, \ {'name': 'diffoff', 'minlen': 5, 'flags': 'BANG|TRLBAR', 'parser': 'parse_cmd_common'}, @@ -2110,6 +2298,7 @@ let s:VimLParser.builtin_commands = [ \ {'name': 'endtry', 'minlen': 4, 'flags': 'TRLBAR|SBOXOK|CMDWIN', 'parser': 'parse_cmd_endtry'}, \ {'name': 'endwhile', 'minlen': 4, 'flags': 'TRLBAR|SBOXOK|CMDWIN', 'parser': 'parse_cmd_endwhile'}, \ {'name': 'enew', 'minlen': 3, 'flags': 'BANG|TRLBAR', 'parser': 'parse_cmd_common'}, + \ {'name': 'eval', 'minlen': 2, 'flags': 'EXTRA|NOTRLCOM|SBOXOK|CMDWIN', 'parser': 'parse_cmd_eval'}, \ {'name': 'ex', 'minlen': 2, 'flags': 'BANG|FILE1|EDITCMD|ARGOPT|TRLBAR', 'parser': 'parse_cmd_common'}, \ {'name': 'execute', 'minlen': 3, 'flags': 'EXTRA|NOTRLCOM|SBOXOK|CMDWIN', 'parser': 'parse_cmd_execute'}, \ {'name': 'exit', 'minlen': 3, 'flags': 'RANGE|WHOLEFOLD|BANG|FILE1|ARGOPT|DFLALL|TRLBAR|CMDWIN', 'parser': 'parse_cmd_common'}, @@ -2185,6 +2374,7 @@ let s:VimLParser.builtin_commands = [ \ {'name': 'left', 'minlen': 2, 'flags': 'TRLBAR|RANGE|WHOLEFOLD|EXTRA|CMDWIN|MODIFY', 'parser': 'parse_cmd_common'}, \ {'name': 'leftabove', 'minlen': 5, 'flags': 'NEEDARG|EXTRA|NOTRLCOM', 'parser': 'parse_cmd_common'}, \ {'name': 'let', 'minlen': 3, 'flags': 'EXTRA|NOTRLCOM|SBOXOK|CMDWIN', 'parser': 'parse_cmd_let'}, + \ {'name': 'const', 'minlen': 4, 'flags': 'EXTRA|NOTRLCOM|SBOXOK|CMDWIN', 'parser': 'parse_cmd_const'}, \ {'name': 'lexpr', 'minlen': 3, 'flags': 'NEEDARG|WORD1|NOTRLCOM|TRLBAR|BANG', 'parser': 'parse_cmd_common'}, \ {'name': 'lfile', 'minlen': 2, 'flags': 'TRLBAR|FILE1|BANG', 'parser': 'parse_cmd_common'}, \ {'name': 'lfirst', 'minlen': 4, 'flags': 'RANGE|NOTADR|COUNT|TRLBAR|BANG', 'parser': 'parse_cmd_common'}, @@ -2521,33 +2711,555 @@ let s:VimLParser.builtin_commands = [ \ {'flags': 'EXTRA|TRLBAR|CMDWIN', 'minlen': 5, 'name': 'tmapclear', 'parser': 'parse_cmd_common'}, \ {'flags': 'EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN', 'minlen': 3, 'name': 'tnoremap', 'parser': 'parse_cmd_common'}, \ {'flags': 'EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN', 'minlen': 5, 'name': 'tunmap', 'parser': 'parse_cmd_common'}, + \ + \ {'flags': 'RANGE|COUNT|TRLBAR', 'minlen': 4, 'name': 'cabove', 'parser': 'parse_cmd_common'}, + \ {'flags': 'RANGE|COUNT|TRLBAR', 'minlen': 3, 'name': 'cafter', 'parser': 'parse_cmd_common'}, + \ {'flags': 'RANGE|COUNT|TRLBAR', 'minlen': 3, 'name': 'cbefore', 'parser': 'parse_cmd_common'}, + \ {'flags': 'RANGE|COUNT|TRLBAR', 'minlen': 4, 'name': 'cbelow', 'parser': 'parse_cmd_common'}, + \ {'flags': 'EXTRA|NOTRLCOM|SBOXOK|CMDWIN', 'minlen': 4, 'name': 'const', 'parser': 'parse_cmd_common'}, + \ {'flags': 'RANGE|COUNT|TRLBAR', 'minlen': 3, 'name': 'labove', 'parser': 'parse_cmd_common'}, + \ {'flags': 'RANGE|COUNT|TRLBAR', 'minlen': 3, 'name': 'lafter', 'parser': 'parse_cmd_common'}, + \ {'flags': 'RANGE|COUNT|TRLBAR', 'minlen': 3, 'name': 'lbefore', 'parser': 'parse_cmd_common'}, + \ {'flags': 'RANGE|COUNT|TRLBAR', 'minlen': 4, 'name': 'lbelow', 'parser': 'parse_cmd_common'}, + \ {'flags': 'TRLBAR|CMDWIN', 'minlen': 7, 'name': 'redrawtabline', 'parser': 'parse_cmd_common'}, + \ {'flags': 'WORD1|TRLBAR|CMDWIN', 'minlen': 7, 'name': 'scriptversion', 'parser': 'parse_cmd_common'}, + \ {'flags': 'BANG|FILE1|TRLBAR|CMDWIN', 'minlen': 2, 'name': 'tcd', 'parser': 'parse_cmd_common'}, + \ {'flags': 'BANG|FILE1|TRLBAR|CMDWIN', 'minlen': 3, 'name': 'tchdir', 'parser': 'parse_cmd_common'}, + \ {'flags': 'RANGE|ZEROR|EXTRA|TRLBAR|NOTRLCOM|CTRLV|CMDWIN', 'minlen': 3, 'name': 'tlmenu', 'parser': 'parse_cmd_common'}, + \ {'flags': 'RANGE|ZEROR|EXTRA|TRLBAR|NOTRLCOM|CTRLV|CMDWIN', 'minlen': 3, 'name': 'tlnoremenu', 'parser': 'parse_cmd_common'}, + \ {'flags': 'RANGE|ZEROR|EXTRA|TRLBAR|NOTRLCOM|CTRLV|CMDWIN', 'minlen': 3, 'name': 'tlunmenu', 'parser': 'parse_cmd_common'}, + \ {'flags': 'EXTRA|TRLBAR|CMDWIN', 'minlen': 2, 'name': 'xrestore', 'parser': 'parse_cmd_common'}, + \ + \ {'flags': 'EXTRA|BANG|SBOXOK|CMDWIN', 'minlen': 3, 'name': 'def', 'parser': 'parse_cmd_common'}, + \ {'flags': 'EXTRA|NEEDARG|TRLBAR|CMDWIN', 'minlen': 4, 'name': 'disassemble', 'parser': 'parse_cmd_common'}, + \ {'flags': 'TRLBAR|CMDWIN', 'minlen': 4, 'name': 'enddef', 'parser': 'parse_cmd_common'}, + \ {'flags': 'EXTRA|NOTRLCOM', 'minlen': 3, 'name': 'export', 'parser': 'parse_cmd_common'}, + \ {'flags': 'EXTRA|NOTRLCOM', 'minlen': 3, 'name': 'import', 'parser': 'parse_cmd_common'}, + \ {'flags': 'BANG|RANGE|NEEDARG|EXTRA|TRLBAR', 'minlen': 7, 'name': 'spellrare', 'parser': 'parse_cmd_common'}, + \ {'flags': '', 'minlen': 4, 'name': 'vim9script', 'parser': 'parse_cmd_common'}, + \] + +" To find new builtin_functions, run the below script. +" $ scripts/update_builtin_functions.sh /path/to/vim/src/evalfunc.c +let s:VimLParser.builtin_functions = [ + \ {'name': 'abs', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'acos', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'add', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'and', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'append', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_LAST'}, + \ {'name': 'appendbufline', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_LAST'}, + \ {'name': 'argc', 'min_argc': 0, 'max_argc': 1, 'argtype': '0'}, + \ {'name': 'argidx', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'arglistid', 'min_argc': 0, 'max_argc': 2, 'argtype': '0'}, + \ {'name': 'argv', 'min_argc': 0, 'max_argc': 2, 'argtype': '0'}, + \ {'name': 'asin', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'assert_beeps', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'assert_equal', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_2'}, + \ {'name': 'assert_equalfile', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'assert_exception', 'min_argc': 1, 'max_argc': 2, 'argtype': '0'}, + \ {'name': 'assert_fails', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'assert_false', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'assert_inrange', 'min_argc': 3, 'max_argc': 4, 'argtype': 'FEARG_3'}, + \ {'name': 'assert_match', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_2'}, + \ {'name': 'assert_notequal', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_2'}, + \ {'name': 'assert_notmatch', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_2'}, + \ {'name': 'assert_report', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'assert_true', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'atan', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'atan2', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'balloon_gettext', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'balloon_show', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'balloon_split', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'browse', 'min_argc': 4, 'max_argc': 4, 'argtype': '0'}, + \ {'name': 'browsedir', 'min_argc': 2, 'max_argc': 2, 'argtype': '0'}, + \ {'name': 'bufadd', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'bufexists', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'buffer_exists', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'buffer_name', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'buffer_number', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'buflisted', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'bufload', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'bufloaded', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'bufname', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'bufnr', 'min_argc': 0, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'bufwinid', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'bufwinnr', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'byte2line', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'byteidx', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'byteidxcomp', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'call', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'ceil', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_canread', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_close', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_close_in', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_evalexpr', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_evalraw', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_getbufnr', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_getjob', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_info', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_log', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_logfile', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_open', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_read', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_readblob', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_readraw', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_sendexpr', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_sendraw', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_setoptions', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'ch_status', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'changenr', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'char2nr', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'chdir', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'cindent', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'clearmatches', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'col', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'complete', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_2'}, + \ {'name': 'complete_add', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'complete_check', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'complete_info', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'confirm', 'min_argc': 1, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'copy', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'cos', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'cosh', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'count', 'min_argc': 2, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'cscope_connection', 'min_argc': 0, 'max_argc': 3, 'argtype': '0'}, + \ {'name': 'cursor', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'debugbreak', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'deepcopy', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'delete', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'deletebufline', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'did_filetype', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'diff_filler', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'diff_hlID', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'echoraw', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'empty', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'environ', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'escape', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'eval', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'eventhandler', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'executable', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'execute', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'exepath', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'exists', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'exp', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'expand', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'expandcmd', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'extend', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'feedkeys', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'file_readable', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'filereadable', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'filewritable', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'filter', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'finddir', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'findfile', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'float2nr', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'floor', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'fmod', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'fnameescape', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'fnamemodify', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'foldclosed', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'foldclosedend', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'foldlevel', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'foldtext', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'foldtextresult', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'foreground', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'funcref', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'function', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'garbagecollect', 'min_argc': 0, 'max_argc': 1, 'argtype': '0'}, + \ {'name': 'get', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'getbufinfo', 'min_argc': 0, 'max_argc': 1, 'argtype': '0'}, + \ {'name': 'getbufline', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'getbufvar', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'getchangelist', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'getchar', 'min_argc': 0, 'max_argc': 1, 'argtype': '0'}, + \ {'name': 'getcharmod', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'getcharsearch', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'getcmdline', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'getcmdpos', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'getcmdtype', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'getcmdwintype', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'getcompletion', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'getcurpos', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'getcwd', 'min_argc': 0, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'getenv', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'getfontname', 'min_argc': 0, 'max_argc': 1, 'argtype': '0'}, + \ {'name': 'getfperm', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'getfsize', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'getftime', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'getftype', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'getimstatus', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'getjumplist', 'min_argc': 0, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'getline', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'getloclist', 'min_argc': 1, 'max_argc': 2, 'argtype': '0'}, + \ {'name': 'getmatches', 'min_argc': 0, 'max_argc': 1, 'argtype': '0'}, + \ {'name': 'getmousepos', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'getpid', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'getpos', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'getqflist', 'min_argc': 0, 'max_argc': 1, 'argtype': '0'}, + \ {'name': 'getreg', 'min_argc': 0, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'getregtype', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'gettabinfo', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'gettabvar', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'gettabwinvar', 'min_argc': 3, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'gettagstack', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'getwininfo', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'getwinpos', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'getwinposx', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'getwinposy', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'getwinvar', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'glob', 'min_argc': 1, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'glob2regpat', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'globpath', 'min_argc': 2, 'max_argc': 5, 'argtype': 'FEARG_2'}, + \ {'name': 'has', 'min_argc': 1, 'max_argc': 1, 'argtype': '0'}, + \ {'name': 'has_key', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'haslocaldir', 'min_argc': 0, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'hasmapto', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'highlightID', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'highlight_exists', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'histadd', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_2'}, + \ {'name': 'histdel', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'histget', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'histnr', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'hlID', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'hlexists', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'hostname', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'iconv', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'indent', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'index', 'min_argc': 2, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'input', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'inputdialog', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'inputlist', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'inputrestore', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'inputsave', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'inputsecret', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'insert', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'interrupt', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'invert', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'isdirectory', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'isinf', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'islocked', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'isnan', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'items', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'job_getchannel', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'job_info', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'job_setoptions', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'job_start', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'job_status', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'job_stop', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'join', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'js_decode', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'js_encode', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'json_decode', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'json_encode', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'keys', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'last_buffer_nr', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'len', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'libcall', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_3'}, + \ {'name': 'libcallnr', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_3'}, + \ {'name': 'line', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'line2byte', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'lispindent', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'list2str', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'listener_add', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_2'}, + \ {'name': 'listener_flush', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'listener_remove', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'localtime', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'log', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'log10', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'luaeval', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'map', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'maparg', 'min_argc': 1, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'mapcheck', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'match', 'min_argc': 2, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'matchadd', 'min_argc': 2, 'max_argc': 5, 'argtype': 'FEARG_1'}, + \ {'name': 'matchaddpos', 'min_argc': 2, 'max_argc': 5, 'argtype': 'FEARG_1'}, + \ {'name': 'matcharg', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'matchdelete', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'matchend', 'min_argc': 2, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'matchlist', 'min_argc': 2, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'matchstr', 'min_argc': 2, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'matchstrpos', 'min_argc': 2, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'max', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'menu_info', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'min', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'mkdir', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'mode', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'mzeval', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'nextnonblank', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'nr2char', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'or', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'pathshorten', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'perleval', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'popup_atcursor', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'popup_beval', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'popup_clear', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'popup_close', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'popup_create', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'popup_dialog', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'popup_filter_menu', 'min_argc': 2, 'max_argc': 2, 'argtype': '0'}, + \ {'name': 'popup_filter_yesno', 'min_argc': 2, 'max_argc': 2, 'argtype': '0'}, + \ {'name': 'popup_findinfo', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'popup_findpreview', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'popup_getoptions', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'popup_getpos', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'popup_hide', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'popup_locate', 'min_argc': 2, 'max_argc': 2, 'argtype': '0'}, + \ {'name': 'popup_menu', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'popup_move', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'popup_notification', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'popup_setoptions', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'popup_settext', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'popup_show', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'pow', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'prevnonblank', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'printf', 'min_argc': 1, 'max_argc': 19, 'argtype': 'FEARG_2'}, + \ {'name': 'prompt_setcallback', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'prompt_setinterrupt', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'prompt_setprompt', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'prop_add', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'prop_clear', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'prop_find', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'prop_list', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'prop_remove', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'prop_type_add', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'prop_type_change', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'prop_type_delete', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'prop_type_get', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'prop_type_list', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'pum_getpos', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'pumvisible', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'py3eval', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'pyeval', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'pyxeval', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'rand', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'range', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'readdir', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'readfile', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'reg_executing', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'reg_recording', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'reltime', 'min_argc': 0, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'reltimefloat', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'reltimestr', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'remote_expr', 'min_argc': 2, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'remote_foreground', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'remote_peek', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'remote_read', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'remote_send', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'remote_startserver', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'remove', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'rename', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'repeat', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'resolve', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'reverse', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'round', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'rubyeval', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'screenattr', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'screenchar', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'screenchars', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'screencol', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'screenpos', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'screenrow', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'screenstring', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'search', 'min_argc': 1, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'searchdecl', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'searchpair', 'min_argc': 3, 'max_argc': 7, 'argtype': '0'}, + \ {'name': 'searchpairpos', 'min_argc': 3, 'max_argc': 7, 'argtype': '0'}, + \ {'name': 'searchpos', 'min_argc': 1, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'server2client', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'serverlist', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'setbufline', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_3'}, + \ {'name': 'setbufvar', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_3'}, + \ {'name': 'setcharsearch', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'setcmdpos', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'setenv', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_2'}, + \ {'name': 'setfperm', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'setline', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_2'}, + \ {'name': 'setloclist', 'min_argc': 2, 'max_argc': 4, 'argtype': 'FEARG_2'}, + \ {'name': 'setmatches', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'setpos', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_2'}, + \ {'name': 'setqflist', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'setreg', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_2'}, + \ {'name': 'settabvar', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_3'}, + \ {'name': 'settabwinvar', 'min_argc': 4, 'max_argc': 4, 'argtype': 'FEARG_4'}, + \ {'name': 'settagstack', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_2'}, + \ {'name': 'setwinvar', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_3'}, + \ {'name': 'sha256', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'shellescape', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'shiftwidth', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'sign_define', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'sign_getdefined', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'sign_getplaced', 'min_argc': 0, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'sign_jump', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'sign_place', 'min_argc': 4, 'max_argc': 5, 'argtype': 'FEARG_1'}, + \ {'name': 'sign_placelist', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'sign_undefine', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'sign_unplace', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'sign_unplacelist', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'simplify', 'min_argc': 1, 'max_argc': 1, 'argtype': '0'}, + \ {'name': 'sin', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'sinh', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'sort', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'sound_clear', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'sound_playevent', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'sound_playfile', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'sound_stop', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'soundfold', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'spellbadword', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'spellsuggest', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'split', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'sqrt', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'srand', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'state', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'str2float', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'str2list', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'str2nr', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'strcharpart', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'strchars', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'strdisplaywidth', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'strftime', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'strgetchar', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'stridx', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'string', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'strlen', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'strpart', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'strptime', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'strridx', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'strtrans', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'strwidth', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'submatch', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'substitute', 'min_argc': 4, 'max_argc': 4, 'argtype': 'FEARG_1'}, + \ {'name': 'swapinfo', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'swapname', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'synID', 'min_argc': 3, 'max_argc': 3, 'argtype': '0'}, + \ {'name': 'synIDattr', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'synIDtrans', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'synconcealed', 'min_argc': 2, 'max_argc': 2, 'argtype': '0'}, + \ {'name': 'synstack', 'min_argc': 2, 'max_argc': 2, 'argtype': '0'}, + \ {'name': 'system', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'systemlist', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'tabpagebuflist', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'tabpagenr', 'min_argc': 0, 'max_argc': 1, 'argtype': '0'}, + \ {'name': 'tabpagewinnr', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'tagfiles', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'taglist', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'tan', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'tanh', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'tempname', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'term_dumpdiff', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'term_dumpload', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'term_dumpwrite', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_2'}, + \ {'name': 'term_getaltscreen', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'term_getansicolors', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'term_getattr', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'term_getcursor', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'term_getjob', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'term_getline', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'term_getscrolled', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'term_getsize', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'term_getstatus', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'term_gettitle', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'term_gettty', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'term_list', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'term_scrape', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'term_sendkeys', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'term_setansicolors', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'term_setapi', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'term_setkill', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'term_setrestore', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'term_setsize', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'term_start', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'term_wait', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'test_alloc_fail', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'test_autochdir', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'test_feedinput', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'test_garbagecollect_now', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'test_garbagecollect_soon', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'test_getvalue', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'test_ignore_error', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'test_null_blob', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'test_null_channel', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'test_null_dict', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'test_null_job', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'test_null_list', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'test_null_partial', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'test_null_string', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'test_option_not_set', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'test_override', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_2'}, + \ {'name': 'test_refcount', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'test_scrollbar', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_2'}, + \ {'name': 'test_setmouse', 'min_argc': 2, 'max_argc': 2, 'argtype': '0'}, + \ {'name': 'test_settime', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'test_srand_seed', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'test_unknown', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'test_void', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'timer_info', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'timer_pause', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'timer_start', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'timer_stop', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'timer_stopall', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'tolower', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'toupper', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'tr', 'min_argc': 3, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'trim', 'min_argc': 1, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'trunc', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'type', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'undofile', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'undotree', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'uniq', 'min_argc': 1, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'values', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'virtcol', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'visualmode', 'min_argc': 0, 'max_argc': 1, 'argtype': '0'}, + \ {'name': 'wildmenumode', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'win_execute', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_2'}, + \ {'name': 'win_findbuf', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'win_getid', 'min_argc': 0, 'max_argc': 2, 'argtype': 'FEARG_1'}, + \ {'name': 'win_gettype', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'win_gotoid', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'win_id2tabwin', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'win_id2win', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'win_screenpos', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'win_splitmove', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'winbufnr', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'wincol', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'windowsversion', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'winheight', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'winlayout', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'winline', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'winnr', 'min_argc': 0, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'winrestcmd', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'winrestview', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'winsaveview', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'winwidth', 'min_argc': 1, 'max_argc': 1, 'argtype': 'FEARG_1'}, + \ {'name': 'wordcount', 'min_argc': 0, 'max_argc': 0, 'argtype': '0'}, + \ {'name': 'writefile', 'min_argc': 2, 'max_argc': 3, 'argtype': 'FEARG_1'}, + \ {'name': 'xor', 'min_argc': 2, 'max_argc': 2, 'argtype': 'FEARG_1'}, \] let s:ExprTokenizer = {} -function! s:ExprTokenizer.new(...) +function! s:ExprTokenizer.new(...) abort let obj = copy(self) call call(obj.__init__, a:000, obj) return obj endfunction -function! s:ExprTokenizer.__init__(reader) +function! s:ExprTokenizer.__init__(reader) abort let self.reader = a:reader let self.cache = {} endfunction -function! s:ExprTokenizer.token(type, value, pos) +function! s:ExprTokenizer.token(type, value, pos) abort return {'type': a:type, 'value': a:value, 'pos': a:pos} endfunction -function! s:ExprTokenizer.peek() +function! s:ExprTokenizer.peek() abort let pos = self.reader.tell() let r = self.get() call self.reader.seek_set(pos) return r endfunction -function! s:ExprTokenizer.get() +function! s:ExprTokenizer.get() abort " FIXME: remove dirty hack if has_key(self.cache, self.reader.tell()) let x = self.cache[self.reader.tell()] @@ -2561,7 +3273,7 @@ function! s:ExprTokenizer.get() return r endfunction -function! s:ExprTokenizer.get2() +function! s:ExprTokenizer.get2() abort let r = self.reader let pos = r.getpos() let c = r.peek() @@ -2577,6 +3289,14 @@ function! s:ExprTokenizer.get2() let s = r.getn(3) let s .= r.read_xdigit() return self.token(s:TOKEN_NUMBER, s, pos) + elseif c ==# '0' && (r.p(1) ==# 'B' || r.p(1) ==# 'b') && (r.p(2) ==# '0' || r.p(2) ==# '1') + let s = r.getn(3) + let s .= r.read_bdigit() + return self.token(s:TOKEN_NUMBER, s, pos) + elseif c ==# '0' && (r.p(1) ==# 'Z' || r.p(1) ==# 'z') && r.p(2) !=# '.' + let s = r.getn(2) + let s .= r.read_blob() + return self.token(s:TOKEN_BLOB, s, pos) elseif s:isdigit(c) let s = r.read_digit() if r.p(0) ==# '.' && s:isdigit(r.p(1)) @@ -2722,9 +3442,12 @@ function! s:ExprTokenizer.get2() if r.p(1) ==# '.' && r.p(2) ==# '.' call r.seek_cur(3) return self.token(s:TOKEN_DOTDOTDOT, '...', pos) + elseif r.p(1) ==# '.' + call r.seek_cur(2) + return self.token(s:TOKEN_DOTDOT, '..', pos) " TODO check scriptversion? else call r.seek_cur(1) - return self.token(s:TOKEN_DOT, '.', pos) + return self.token(s:TOKEN_DOT, '.', pos) " TODO check scriptversion? endif elseif c ==# '*' call r.seek_cur(1) @@ -2745,8 +3468,13 @@ function! s:ExprTokenizer.get2() call r.seek_cur(1) return self.token(s:TOKEN_COLON, ':', pos) elseif c ==# '#' - call r.seek_cur(1) - return self.token(s:TOKEN_SHARP, '#', pos) + if r.p(1) ==# '{' + call r.seek_cur(2) + return self.token(s:TOKEN_LITCOPEN, '#{', pos) + else + call r.seek_cur(1) + return self.token(s:TOKEN_SHARP, '#', pos) + endif elseif c ==# '(' call r.seek_cur(1) return self.token(s:TOKEN_POPEN, '(', pos) @@ -2806,7 +3534,7 @@ function! s:ExprTokenizer.get2() endif endfunction -function! s:ExprTokenizer.get_sstring() +function! s:ExprTokenizer.get_sstring() abort call self.reader.skip_white() let c = self.reader.p(0) if c !=# "'" @@ -2834,7 +3562,7 @@ function! s:ExprTokenizer.get_sstring() return s endfunction -function! s:ExprTokenizer.get_dstring() +function! s:ExprTokenizer.get_dstring() abort call self.reader.skip_white() let c = self.reader.p(0) if c !=# '"' @@ -2866,35 +3594,60 @@ function! s:ExprTokenizer.get_dstring() return s endfunction +function! s:ExprTokenizer.parse_dict_literal_key() abort + call self.reader.skip_white() + let c = self.reader.peek() + if !s:isalnum(c) && c !=# '_' && c !=# '-' + throw s:Err(printf('unexpected character: %s', c), self.reader.getpos()) + endif + let node = s:Node(s:NODE_STRING) + let s = c + call self.reader.seek_cur(1) + let node.pos = self.reader.getpos() + while s:TRUE + let c = self.reader.p(0) + if c ==# '' || c ==# '' + throw s:Err('unexpectd EOL', self.reader.getpos()) + endif + if !s:isalnum(c) && c !=# '_' && c !=# '-' + break + endif + call self.reader.seek_cur(1) + let s .= c + endwhile + let node.value = "'" . s . "'" + return node +endfunction + let s:ExprParser = {} -function! s:ExprParser.new(...) +function! s:ExprParser.new(...) abort let obj = copy(self) call call(obj.__init__, a:000, obj) return obj endfunction -function! s:ExprParser.__init__(reader) +function! s:ExprParser.__init__(reader) abort let self.reader = a:reader let self.tokenizer = s:ExprTokenizer.new(a:reader) endfunction -function! s:ExprParser.parse() +function! s:ExprParser.parse() abort return self.parse_expr1() endfunction " expr1: expr2 ? expr1 : expr1 -function! s:ExprParser.parse_expr1() +function! s:ExprParser.parse_expr1() abort let left = self.parse_expr2() let pos = self.reader.tell() let token = self.tokenizer.get() - if token.type == s:TOKEN_QUESTION + if token.type ==# s:TOKEN_QUESTION let node = s:Node(s:NODE_TERNARY) let node.pos = token.pos let node.cond = left let node.left = self.parse_expr1() let token = self.tokenizer.get() - if token.type != s:TOKEN_COLON + if token.type !=# s:TOKEN_COLON throw s:Err(printf('unexpected token: %s', token.value), token.pos) endif let node.right = self.parse_expr1() @@ -2906,12 +3659,12 @@ function! s:ExprParser.parse_expr1() endfunction " expr2: expr3 || expr3 .. -function! s:ExprParser.parse_expr2() +function! s:ExprParser.parse_expr2() abort let left = self.parse_expr3() while s:TRUE let pos = self.reader.tell() let token = self.tokenizer.get() - if token.type == s:TOKEN_OROR + if token.type ==# s:TOKEN_OROR let node = s:Node(s:NODE_OR) let node.pos = token.pos let node.left = left @@ -2926,12 +3679,12 @@ function! s:ExprParser.parse_expr2() endfunction " expr3: expr4 && expr4 -function! s:ExprParser.parse_expr3() +function! s:ExprParser.parse_expr3() abort let left = self.parse_expr4() while s:TRUE let pos = self.reader.tell() let token = self.tokenizer.get() - if token.type == s:TOKEN_ANDAND + if token.type ==# s:TOKEN_ANDAND let node = s:Node(s:NODE_AND) let node.pos = token.pos let node.left = left @@ -2960,185 +3713,185 @@ endfunction " " expr5 is expr5 " expr5 isnot expr5 -function! s:ExprParser.parse_expr4() +function! s:ExprParser.parse_expr4() abort let left = self.parse_expr5() let pos = self.reader.tell() let token = self.tokenizer.get() - if token.type == s:TOKEN_EQEQ + if token.type ==# s:TOKEN_EQEQ let node = s:Node(s:NODE_EQUAL) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_EQEQCI + elseif token.type ==# s:TOKEN_EQEQCI let node = s:Node(s:NODE_EQUALCI) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_EQEQCS + elseif token.type ==# s:TOKEN_EQEQCS let node = s:Node(s:NODE_EQUALCS) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_NEQ + elseif token.type ==# s:TOKEN_NEQ let node = s:Node(s:NODE_NEQUAL) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_NEQCI + elseif token.type ==# s:TOKEN_NEQCI let node = s:Node(s:NODE_NEQUALCI) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_NEQCS + elseif token.type ==# s:TOKEN_NEQCS let node = s:Node(s:NODE_NEQUALCS) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_GT + elseif token.type ==# s:TOKEN_GT let node = s:Node(s:NODE_GREATER) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_GTCI + elseif token.type ==# s:TOKEN_GTCI let node = s:Node(s:NODE_GREATERCI) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_GTCS + elseif token.type ==# s:TOKEN_GTCS let node = s:Node(s:NODE_GREATERCS) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_GTEQ + elseif token.type ==# s:TOKEN_GTEQ let node = s:Node(s:NODE_GEQUAL) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_GTEQCI + elseif token.type ==# s:TOKEN_GTEQCI let node = s:Node(s:NODE_GEQUALCI) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_GTEQCS + elseif token.type ==# s:TOKEN_GTEQCS let node = s:Node(s:NODE_GEQUALCS) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_LT + elseif token.type ==# s:TOKEN_LT let node = s:Node(s:NODE_SMALLER) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_LTCI + elseif token.type ==# s:TOKEN_LTCI let node = s:Node(s:NODE_SMALLERCI) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_LTCS + elseif token.type ==# s:TOKEN_LTCS let node = s:Node(s:NODE_SMALLERCS) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_LTEQ + elseif token.type ==# s:TOKEN_LTEQ let node = s:Node(s:NODE_SEQUAL) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_LTEQCI + elseif token.type ==# s:TOKEN_LTEQCI let node = s:Node(s:NODE_SEQUALCI) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_LTEQCS + elseif token.type ==# s:TOKEN_LTEQCS let node = s:Node(s:NODE_SEQUALCS) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_MATCH + elseif token.type ==# s:TOKEN_MATCH let node = s:Node(s:NODE_MATCH) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_MATCHCI + elseif token.type ==# s:TOKEN_MATCHCI let node = s:Node(s:NODE_MATCHCI) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_MATCHCS + elseif token.type ==# s:TOKEN_MATCHCS let node = s:Node(s:NODE_MATCHCS) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_NOMATCH + elseif token.type ==# s:TOKEN_NOMATCH let node = s:Node(s:NODE_NOMATCH) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_NOMATCHCI + elseif token.type ==# s:TOKEN_NOMATCHCI let node = s:Node(s:NODE_NOMATCHCI) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_NOMATCHCS + elseif token.type ==# s:TOKEN_NOMATCHCS let node = s:Node(s:NODE_NOMATCHCS) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_IS + elseif token.type ==# s:TOKEN_IS let node = s:Node(s:NODE_IS) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_ISCI + elseif token.type ==# s:TOKEN_ISCI let node = s:Node(s:NODE_ISCI) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_ISCS + elseif token.type ==# s:TOKEN_ISCS let node = s:Node(s:NODE_ISCS) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_ISNOT + elseif token.type ==# s:TOKEN_ISNOT let node = s:Node(s:NODE_ISNOT) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_ISNOTCI + elseif token.type ==# s:TOKEN_ISNOTCI let node = s:Node(s:NODE_ISNOTCI) let node.pos = token.pos let node.left = left let node.right = self.parse_expr5() let left = node - elseif token.type == s:TOKEN_ISNOTCS + elseif token.type ==# s:TOKEN_ISNOTCS let node = s:Node(s:NODE_ISNOTCS) let node.pos = token.pos let node.left = left @@ -3153,24 +3906,31 @@ endfunction " expr5: expr6 + expr6 .. " expr6 - expr6 .. " expr6 . expr6 .. -function! s:ExprParser.parse_expr5() +" expr6 .. expr6 .. +function! s:ExprParser.parse_expr5() abort let left = self.parse_expr6() while s:TRUE let pos = self.reader.tell() let token = self.tokenizer.get() - if token.type == s:TOKEN_PLUS + if token.type ==# s:TOKEN_PLUS let node = s:Node(s:NODE_ADD) let node.pos = token.pos let node.left = left let node.right = self.parse_expr6() let left = node - elseif token.type == s:TOKEN_MINUS + elseif token.type ==# s:TOKEN_MINUS let node = s:Node(s:NODE_SUBTRACT) let node.pos = token.pos let node.left = left let node.right = self.parse_expr6() let left = node - elseif token.type == s:TOKEN_DOT + elseif token.type ==# s:TOKEN_DOTDOT " TODO check scriptversion? + let node = s:Node(s:NODE_CONCAT) + let node.pos = token.pos + let node.left = left + let node.right = self.parse_expr6() + let left = node + elseif token.type ==# s:TOKEN_DOT " TODO check scriptversion? let node = s:Node(s:NODE_CONCAT) let node.pos = token.pos let node.left = left @@ -3187,24 +3947,24 @@ endfunction " expr6: expr7 * expr7 .. " expr7 / expr7 .. " expr7 % expr7 .. -function! s:ExprParser.parse_expr6() +function! s:ExprParser.parse_expr6() abort let left = self.parse_expr7() while s:TRUE let pos = self.reader.tell() let token = self.tokenizer.get() - if token.type == s:TOKEN_STAR + if token.type ==# s:TOKEN_STAR let node = s:Node(s:NODE_MULTIPLY) let node.pos = token.pos let node.left = left let node.right = self.parse_expr7() let left = node - elseif token.type == s:TOKEN_SLASH + elseif token.type ==# s:TOKEN_SLASH let node = s:Node(s:NODE_DIVIDE) let node.pos = token.pos let node.left = left let node.right = self.parse_expr7() let left = node - elseif token.type == s:TOKEN_PERCENT + elseif token.type ==# s:TOKEN_PERCENT let node = s:Node(s:NODE_REMAINDER) let node.pos = token.pos let node.left = left @@ -3221,20 +3981,20 @@ endfunction " expr7: ! expr7 " - expr7 " + expr7 -function! s:ExprParser.parse_expr7() +function! s:ExprParser.parse_expr7() abort let pos = self.reader.tell() let token = self.tokenizer.get() - if token.type == s:TOKEN_NOT + if token.type ==# s:TOKEN_NOT let node = s:Node(s:NODE_NOT) let node.pos = token.pos let node.left = self.parse_expr7() return node - elseif token.type == s:TOKEN_MINUS + elseif token.type ==# s:TOKEN_MINUS let node = s:Node(s:NODE_MINUS) let node.pos = token.pos let node.left = self.parse_expr7() return node - elseif token.type == s:TOKEN_PLUS + elseif token.type ==# s:TOKEN_PLUS let node = s:Node(s:NODE_PLUS) let node.pos = token.pos let node.left = self.parse_expr7() @@ -3249,44 +4009,47 @@ endfunction " expr8: expr8[expr1] " expr8[expr1 : expr1] " expr8.name +" expr8->name(expr1, ...) +" expr8->s:user_func(expr1, ...) +" expr8->{lambda}(expr1, ...) " expr8(expr1, ...) -function! s:ExprParser.parse_expr8() +function! s:ExprParser.parse_expr8() abort let left = self.parse_expr9() while s:TRUE let pos = self.reader.tell() let c = self.reader.peek() let token = self.tokenizer.get() - if !s:iswhite(c) && token.type == s:TOKEN_SQOPEN + if !s:iswhite(c) && token.type ==# s:TOKEN_SQOPEN let npos = token.pos - if self.tokenizer.peek().type == s:TOKEN_COLON + if self.tokenizer.peek().type ==# s:TOKEN_COLON call self.tokenizer.get() let node = s:Node(s:NODE_SLICE) let node.pos = npos let node.left = left let node.rlist = [s:NIL, s:NIL] let token = self.tokenizer.peek() - if token.type != s:TOKEN_SQCLOSE + if token.type !=# s:TOKEN_SQCLOSE let node.rlist[1] = self.parse_expr1() endif let token = self.tokenizer.get() - if token.type != s:TOKEN_SQCLOSE + if token.type !=# s:TOKEN_SQCLOSE throw s:Err(printf('unexpected token: %s', token.value), token.pos) endif let left = node else let right = self.parse_expr1() - if self.tokenizer.peek().type == s:TOKEN_COLON + if self.tokenizer.peek().type ==# s:TOKEN_COLON call self.tokenizer.get() let node = s:Node(s:NODE_SLICE) let node.pos = npos let node.left = left let node.rlist = [right, s:NIL] let token = self.tokenizer.peek() - if token.type != s:TOKEN_SQCLOSE + if token.type !=# s:TOKEN_SQCLOSE let node.rlist[1] = self.parse_expr1() endif let token = self.tokenizer.get() - if token.type != s:TOKEN_SQCLOSE + if token.type !=# s:TOKEN_SQCLOSE throw s:Err(printf('unexpected token: %s', token.value), token.pos) endif let left = node @@ -3296,46 +4059,39 @@ function! s:ExprParser.parse_expr8() let node.left = left let node.right = right let token = self.tokenizer.get() - if token.type != s:TOKEN_SQCLOSE + if token.type !=# s:TOKEN_SQCLOSE throw s:Err(printf('unexpected token: %s', token.value), token.pos) endif let left = node endif endif unlet node - elseif token.type == s:TOKEN_POPEN + elseif token.type ==# s:TOKEN_ARROW + let funcname_or_lambda = self.parse_expr9() + let token = self.tokenizer.get() + if token.type !=# s:TOKEN_POPEN + throw s:Err('E107: Missing parentheses: lambda', token.pos) + endif + let right = s:Node(s:NODE_CALL) + let right.pos = token.pos + let right.left = funcname_or_lambda + let right.rlist = self.parse_rlist() + let node = s:Node(s:NODE_METHOD) + let node.pos = token.pos + let node.left = left + let node.right = right + let left = node + unlet node + elseif token.type ==# s:TOKEN_POPEN let node = s:Node(s:NODE_CALL) let node.pos = token.pos let node.left = left - let node.rlist = [] - if self.tokenizer.peek().type == s:TOKEN_PCLOSE - call self.tokenizer.get() - else - while s:TRUE - call add(node.rlist, self.parse_expr1()) - let token = self.tokenizer.get() - if token.type == s:TOKEN_COMMA - " XXX: Vim allows foo(a, b, ). Lint should warn it. - if self.tokenizer.peek().type == s:TOKEN_PCLOSE - call self.tokenizer.get() - break - endif - elseif token.type == s:TOKEN_PCLOSE - break - else - throw s:Err(printf('unexpected token: %s', token.value), token.pos) - endif - endwhile - endif - if len(node.rlist) > s:MAX_FUNC_ARGS - " TODO: funcname E740: Too many arguments for function: %s - throw s:Err('E740: Too many arguments for function', node.pos) - endif + let node.rlist = self.parse_rlist() let left = node unlet node - elseif !s:iswhite(c) && token.type == s:TOKEN_DOT + elseif !s:iswhite(c) && token.type ==# s:TOKEN_DOT " TODO check scriptversion? let node = self.parse_dot(token, left) - if node is s:NIL + if node is# s:NIL call self.reader.seek_set(pos) break endif @@ -3349,11 +4105,41 @@ function! s:ExprParser.parse_expr8() return left endfunction +function! s:ExprParser.parse_rlist() abort + let rlist = [] + let token = self.tokenizer.peek() + if self.tokenizer.peek().type ==# s:TOKEN_PCLOSE + call self.tokenizer.get() + else + while s:TRUE + call add(rlist, self.parse_expr1()) + let token = self.tokenizer.get() + if token.type ==# s:TOKEN_COMMA + " XXX: Vim allows foo(a, b, ). Lint should warn it. + if self.tokenizer.peek().type ==# s:TOKEN_PCLOSE + call self.tokenizer.get() + break + endif + elseif token.type ==# s:TOKEN_PCLOSE + break + else + throw s:Err(printf('unexpected token: %s', token.value), token.pos) + endif + endwhile + endif + if len(rlist) > s:MAX_FUNC_ARGS + " TODO: funcname E740: Too many arguments for function: %s + throw s:Err('E740: Too many arguments for function', token.pos) + endif + return rlist +endfunction + " expr9: number " "string" " 'string' " [expr1, ...] " {expr1: expr1, ...} +" #{literal_key1: expr1, ...} " {args -> expr1} " &option " (expr1) @@ -3363,42 +4149,46 @@ endfunction " @r " function(expr1, ...) " func{ti}on(expr1, ...) -function! s:ExprParser.parse_expr9() +function! s:ExprParser.parse_expr9() abort let pos = self.reader.tell() let token = self.tokenizer.get() let node = s:Node(-1) - if token.type == s:TOKEN_NUMBER + if token.type ==# s:TOKEN_NUMBER let node = s:Node(s:NODE_NUMBER) let node.pos = token.pos let node.value = token.value - elseif token.type == s:TOKEN_DQUOTE + elseif token.type ==# s:TOKEN_BLOB + let node = s:Node(s:NODE_BLOB) + let node.pos = token.pos + let node.value = token.value + elseif token.type ==# s:TOKEN_DQUOTE call self.reader.seek_set(pos) let node = s:Node(s:NODE_STRING) let node.pos = token.pos let node.value = '"' . self.tokenizer.get_dstring() . '"' - elseif token.type == s:TOKEN_SQUOTE + elseif token.type ==# s:TOKEN_SQUOTE call self.reader.seek_set(pos) let node = s:Node(s:NODE_STRING) let node.pos = token.pos let node.value = "'" . self.tokenizer.get_sstring() . "'" - elseif token.type == s:TOKEN_SQOPEN + elseif token.type ==# s:TOKEN_SQOPEN let node = s:Node(s:NODE_LIST) let node.pos = token.pos let node.value = [] let token = self.tokenizer.peek() - if token.type == s:TOKEN_SQCLOSE + if token.type ==# s:TOKEN_SQCLOSE call self.tokenizer.get() else while s:TRUE call add(node.value, self.parse_expr1()) let token = self.tokenizer.peek() - if token.type == s:TOKEN_COMMA + if token.type ==# s:TOKEN_COMMA call self.tokenizer.get() - if self.tokenizer.peek().type == s:TOKEN_SQCLOSE + if self.tokenizer.peek().type ==# s:TOKEN_SQCLOSE call self.tokenizer.get() break endif - elseif token.type == s:TOKEN_SQCLOSE + elseif token.type ==# s:TOKEN_SQCLOSE call self.tokenizer.get() break else @@ -3406,16 +4196,17 @@ function! s:ExprParser.parse_expr9() endif endwhile endif - elseif token.type == s:TOKEN_COPEN + elseif token.type ==# s:TOKEN_COPEN || token.type ==# s:TOKEN_LITCOPEN + let is_litdict = token.type ==# s:TOKEN_LITCOPEN let savepos = self.reader.tell() let nodepos = token.pos let token = self.tokenizer.get() - let lambda = token.type == s:TOKEN_ARROW - if !lambda && !(token.type == s:TOKEN_SQUOTE || token.type == s:TOKEN_DQUOTE) + let lambda = token.type ==# s:TOKEN_ARROW + if !lambda && !(token.type ==# s:TOKEN_SQUOTE || token.type ==# s:TOKEN_DQUOTE) " if the token type is stirng, we cannot peek next token and we can " assume it's not lambda. let token2 = self.tokenizer.peek() - let lambda = token2.type == s:TOKEN_ARROW || token2.type == s:TOKEN_COMMA + let lambda = token2.type ==# s:TOKEN_ARROW || token2.type ==# s:TOKEN_COMMA endif " fallback to dict or {expr} if true let fallback = s:FALSE @@ -3426,9 +4217,9 @@ function! s:ExprParser.parse_expr9() let node.rlist = [] let named = {} while s:TRUE - if token.type == s:TOKEN_ARROW + if token.type ==# s:TOKEN_ARROW break - elseif token.type == s:TOKEN_IDENTIFIER + elseif token.type ==# s:TOKEN_IDENTIFIER if !s:isargname(token.value) throw s:Err(printf('E125: Illegal argument: %s', token.value), token.pos) elseif has_key(named, token.value) @@ -3439,30 +4230,30 @@ function! s:ExprParser.parse_expr9() let varnode.pos = token.pos let varnode.value = token.value " XXX: Vim doesn't skip white space before comma. {a ,b -> ...} => E475 - if s:iswhite(self.reader.p(0)) && self.tokenizer.peek().type == s:TOKEN_COMMA + if s:iswhite(self.reader.p(0)) && self.tokenizer.peek().type ==# s:TOKEN_COMMA throw s:Err('E475: Invalid argument: White space is not allowed before comma', self.reader.getpos()) endif let token = self.tokenizer.get() call add(node.rlist, varnode) - if token.type == s:TOKEN_COMMA + if token.type ==# s:TOKEN_COMMA " XXX: Vim allows last comma. {a, b, -> ...} => OK let token = self.tokenizer.peek() - if token.type == s:TOKEN_ARROW + if token.type ==# s:TOKEN_ARROW call self.tokenizer.get() break endif - elseif token.type == s:TOKEN_ARROW + elseif token.type ==# s:TOKEN_ARROW break else throw s:Err(printf('unexpected token: %s, type: %d', token.value, token.type), token.pos) endif - elseif token.type == s:TOKEN_DOTDOTDOT + elseif token.type ==# s:TOKEN_DOTDOTDOT let varnode = s:Node(s:NODE_IDENTIFIER) let varnode.pos = token.pos let varnode.value = token.value call add(node.rlist, varnode) let token = self.tokenizer.peek() - if token.type == s:TOKEN_ARROW + if token.type ==# s:TOKEN_ARROW call self.tokenizer.get() break else @@ -3477,7 +4268,7 @@ function! s:ExprParser.parse_expr9() if !fallback let node.left = self.parse_expr1() let token = self.tokenizer.get() - if token.type != s:TOKEN_CCLOSE + if token.type !=# s:TOKEN_CCLOSE throw s:Err(printf('unexpected token: %s', token.value), token.pos) endif return node @@ -3489,14 +4280,14 @@ function! s:ExprParser.parse_expr9() let node.value = [] call self.reader.seek_set(savepos) let token = self.tokenizer.peek() - if token.type == s:TOKEN_CCLOSE + if token.type ==# s:TOKEN_CCLOSE call self.tokenizer.get() return node endif while 1 - let key = self.parse_expr1() + let key = is_litdict ? self.tokenizer.parse_dict_literal_key() : self.parse_expr1() let token = self.tokenizer.get() - if token.type == s:TOKEN_CCLOSE + if token.type ==# s:TOKEN_CCLOSE if !empty(node.value) throw s:Err(printf('unexpected token: %s', token.value), token.pos) endif @@ -3504,54 +4295,54 @@ function! s:ExprParser.parse_expr9() let node = self.parse_identifier() break endif - if token.type != s:TOKEN_COLON + if token.type !=# s:TOKEN_COLON throw s:Err(printf('unexpected token: %s', token.value), token.pos) endif let val = self.parse_expr1() call add(node.value, [key, val]) let token = self.tokenizer.get() - if token.type == s:TOKEN_COMMA - if self.tokenizer.peek().type == s:TOKEN_CCLOSE + if token.type ==# s:TOKEN_COMMA + if self.tokenizer.peek().type ==# s:TOKEN_CCLOSE call self.tokenizer.get() break endif - elseif token.type == s:TOKEN_CCLOSE + elseif token.type ==# s:TOKEN_CCLOSE break else throw s:Err(printf('unexpected token: %s', token.value), token.pos) endif endwhile return node - elseif token.type == s:TOKEN_POPEN + elseif token.type ==# s:TOKEN_POPEN let node = s:Node(s:NODE_PARENEXPR) let node.pos = token.pos let node.value = self.parse_expr1() let token = self.tokenizer.get() - if token.type != s:TOKEN_PCLOSE + if token.type !=# s:TOKEN_PCLOSE throw s:Err(printf('unexpected token: %s', token.value), token.pos) endif - elseif token.type == s:TOKEN_OPTION + elseif token.type ==# s:TOKEN_OPTION let node = s:Node(s:NODE_OPTION) let node.pos = token.pos let node.value = token.value - elseif token.type == s:TOKEN_IDENTIFIER + elseif token.type ==# s:TOKEN_IDENTIFIER call self.reader.seek_set(pos) let node = self.parse_identifier() - elseif s:FALSE && (token.type == s:TOKEN_COLON || token.type == s:TOKEN_SHARP) + elseif s:FALSE && (token.type ==# s:TOKEN_COLON || token.type ==# s:TOKEN_SHARP) " XXX: no parse error but invalid expression call self.reader.seek_set(pos) let node = self.parse_identifier() - elseif token.type == s:TOKEN_LT && self.reader.peekn(4) ==? 'SID>' + elseif token.type ==# s:TOKEN_LT && self.reader.peekn(4) ==? 'SID>' call self.reader.seek_set(pos) let node = self.parse_identifier() - elseif token.type == s:TOKEN_IS || token.type == s:TOKEN_ISCS || token.type == s:TOKEN_ISNOT || token.type == s:TOKEN_ISNOTCS + elseif token.type ==# s:TOKEN_IS || token.type ==# s:TOKEN_ISCS || token.type ==# s:TOKEN_ISNOT || token.type ==# s:TOKEN_ISNOTCS call self.reader.seek_set(pos) let node = self.parse_identifier() - elseif token.type == s:TOKEN_ENV + elseif token.type ==# s:TOKEN_ENV let node = s:Node(s:NODE_ENV) let node.pos = token.pos let node.value = token.value - elseif token.type == s:TOKEN_REG + elseif token.type ==# s:TOKEN_REG let node = s:Node(s:NODE_REG) let node.pos = token.pos let node.value = token.value @@ -3564,8 +4355,8 @@ endfunction " SUBSCRIPT or CONCAT " dict "." [0-9A-Za-z_]+ => (subscript dict key) " str "." expr6 => (concat str expr6) -function! s:ExprParser.parse_dot(token, left) - if a:left.type != s:NODE_IDENTIFIER && a:left.type != s:NODE_CURLYNAME && a:left.type != s:NODE_DICT && a:left.type != s:NODE_SUBSCRIPT && a:left.type != s:NODE_CALL && a:left.type != s:NODE_DOT +function! s:ExprParser.parse_dot(token, left) abort + if a:left.type !=# s:NODE_IDENTIFIER && a:left.type !=# s:NODE_CURLYNAME && a:left.type !=# s:NODE_DICT && a:left.type !=# s:NODE_SUBSCRIPT && a:left.type !=# s:NODE_CALL && a:left.type !=# s:NODE_DOT return s:NIL endif if !s:iswordc(self.reader.p(0)) @@ -3587,11 +4378,36 @@ function! s:ExprParser.parse_dot(token, left) return node endfunction -function! s:ExprParser.parse_identifier() +" CONCAT +" str ".." expr6 => (concat str expr6) +function! s:ExprParser.parse_concat(token, left) abort + if a:left.type !=# s:NODE_IDENTIFIER && a:left.type !=# s:NODE_CURLYNAME && a:left.type !=# s:NODE_DICT && a:left.type !=# s:NODE_SUBSCRIPT && a:left.type !=# s:NODE_CALL && a:left.type !=# s:NODE_DOT + return s:NIL + endif + if !s:iswordc(self.reader.p(0)) + return s:NIL + endif + let pos = self.reader.getpos() + let name = self.reader.read_word() + if s:isnamec(self.reader.p(0)) + " XXX: foo is str => ok, foo is obj => invalid expression + " foo.s:bar or foo.bar#baz + return s:NIL + endif + let node = s:Node(s:NODE_CONCAT) + let node.pos = a:token.pos + let node.left = a:left + let node.right = s:Node(s:NODE_IDENTIFIER) + let node.right.pos = pos + let node.right.value = name + return node +endfunction + +function! s:ExprParser.parse_identifier() abort call self.reader.skip_white() let npos = self.reader.getpos() let curly_parts = self.parse_curly_parts() - if len(curly_parts) == 1 && curly_parts[0].type == s:NODE_CURLYNAMEPART + if len(curly_parts) ==# 1 && curly_parts[0].type ==# s:NODE_CURLYNAMEPART let node = s:Node(s:NODE_IDENTIFIER) let node.pos = npos let node.value = curly_parts[0].value @@ -3604,7 +4420,7 @@ function! s:ExprParser.parse_identifier() endif endfunction -function! s:ExprParser.parse_curly_parts() +function! s:ExprParser.parse_curly_parts() abort let curly_parts = [] let c = self.reader.peek() let pos = self.reader.getpos() @@ -3649,50 +4465,50 @@ endfunction let s:LvalueParser = copy(s:ExprParser) -function! s:LvalueParser.parse() +function! s:LvalueParser.parse() abort return self.parse_lv8() endfunction " expr8: expr8[expr1] " expr8[expr1 : expr1] " expr8.name -function! s:LvalueParser.parse_lv8() +function! s:LvalueParser.parse_lv8() abort let left = self.parse_lv9() while s:TRUE let pos = self.reader.tell() let c = self.reader.peek() let token = self.tokenizer.get() - if !s:iswhite(c) && token.type == s:TOKEN_SQOPEN + if !s:iswhite(c) && token.type ==# s:TOKEN_SQOPEN let npos = token.pos let node = s:Node(-1) - if self.tokenizer.peek().type == s:TOKEN_COLON + if self.tokenizer.peek().type ==# s:TOKEN_COLON call self.tokenizer.get() let node = s:Node(s:NODE_SLICE) let node.pos = npos let node.left = left let node.rlist = [s:NIL, s:NIL] let token = self.tokenizer.peek() - if token.type != s:TOKEN_SQCLOSE + if token.type !=# s:TOKEN_SQCLOSE let node.rlist[1] = self.parse_expr1() endif let token = self.tokenizer.get() - if token.type != s:TOKEN_SQCLOSE + if token.type !=# s:TOKEN_SQCLOSE throw s:Err(printf('unexpected token: %s', token.value), token.pos) endif else let right = self.parse_expr1() - if self.tokenizer.peek().type == s:TOKEN_COLON + if self.tokenizer.peek().type ==# s:TOKEN_COLON call self.tokenizer.get() let node = s:Node(s:NODE_SLICE) let node.pos = npos let node.left = left let node.rlist = [right, s:NIL] let token = self.tokenizer.peek() - if token.type != s:TOKEN_SQCLOSE + if token.type !=# s:TOKEN_SQCLOSE let node.rlist[1] = self.parse_expr1() endif let token = self.tokenizer.get() - if token.type != s:TOKEN_SQCLOSE + if token.type !=# s:TOKEN_SQCLOSE throw s:Err(printf('unexpected token: %s', token.value), token.pos) endif else @@ -3701,16 +4517,16 @@ function! s:LvalueParser.parse_lv8() let node.left = left let node.right = right let token = self.tokenizer.get() - if token.type != s:TOKEN_SQCLOSE + if token.type !=# s:TOKEN_SQCLOSE throw s:Err(printf('unexpected token: %s', token.value), token.pos) endif endif endif let left = node unlet node - elseif !s:iswhite(c) && token.type == s:TOKEN_DOT + elseif !s:iswhite(c) && token.type ==# s:TOKEN_DOT let node = self.parse_dot(token, left) - if node is s:NIL + if node is# s:NIL call self.reader.seek_set(pos) break endif @@ -3729,28 +4545,28 @@ endfunction " var{ria}ble " $VAR " @r -function! s:LvalueParser.parse_lv9() +function! s:LvalueParser.parse_lv9() abort let pos = self.reader.tell() let token = self.tokenizer.get() let node = s:Node(-1) - if token.type == s:TOKEN_COPEN + if token.type ==# s:TOKEN_COPEN call self.reader.seek_set(pos) let node = self.parse_identifier() - elseif token.type == s:TOKEN_OPTION + elseif token.type ==# s:TOKEN_OPTION let node = s:Node(s:NODE_OPTION) let node.pos = token.pos let node.value = token.value - elseif token.type == s:TOKEN_IDENTIFIER + elseif token.type ==# s:TOKEN_IDENTIFIER call self.reader.seek_set(pos) let node = self.parse_identifier() - elseif token.type == s:TOKEN_LT && self.reader.peekn(4) ==? 'SID>' + elseif token.type ==# s:TOKEN_LT && self.reader.peekn(4) ==? 'SID>' call self.reader.seek_set(pos) let node = self.parse_identifier() - elseif token.type == s:TOKEN_ENV + elseif token.type ==# s:TOKEN_ENV let node = s:Node(s:NODE_ENV) let node.pos = token.pos let node.value = token.value - elseif token.type == s:TOKEN_REG + elseif token.type ==# s:TOKEN_REG let node = s:Node(s:NODE_REG) let node.pos = token.pos let node.pos = token.pos @@ -3763,13 +4579,13 @@ endfunction let s:StringReader = {} -function! s:StringReader.new(...) +function! s:StringReader.new(...) abort let obj = copy(self) call call(obj.__init__, a:000, obj) return obj endfunction -function! s:StringReader.__init__(lines) +function! s:StringReader.__init__(lines) abort let self.buf = [] let self.pos = [] let lnum = 0 @@ -3787,7 +4603,7 @@ function! s:StringReader.__init__(lines) let col = 0 for c in split(a:lines[lnum + 1], '\zs') if skip - if c == '\' + if c ==# '\' let skip = s:FALSE endif else @@ -3810,41 +4626,41 @@ function! s:StringReader.__init__(lines) let self.i = 0 endfunction -function! s:StringReader.eof() +function! s:StringReader.eof() abort return self.i >= len(self.buf) endfunction -function! s:StringReader.tell() +function! s:StringReader.tell() abort return self.i endfunction -function! s:StringReader.seek_set(i) +function! s:StringReader.seek_set(i) abort let self.i = a:i endfunction -function! s:StringReader.seek_cur(i) +function! s:StringReader.seek_cur(i) abort let self.i = self.i + a:i endfunction -function! s:StringReader.seek_end(i) +function! s:StringReader.seek_end(i) abort let self.i = len(self.buf) + a:i endfunction -function! s:StringReader.p(i) +function! s:StringReader.p(i) abort if self.i >= len(self.buf) return '' endif return self.buf[self.i + a:i] endfunction -function! s:StringReader.peek() +function! s:StringReader.peek() abort if self.i >= len(self.buf) return '' endif return self.buf[self.i] endfunction -function! s:StringReader.get() +function! s:StringReader.get() abort if self.i >= len(self.buf) return '' endif @@ -3852,14 +4668,14 @@ function! s:StringReader.get() return self.buf[self.i - 1] endfunction -function! s:StringReader.peekn(n) +function! s:StringReader.peekn(n) abort let pos = self.tell() let r = self.getn(a:n) call self.seek_set(pos) return r endfunction -function! s:StringReader.getn(n) +function! s:StringReader.getn(n) abort let r = '' let j = 0 while self.i < len(self.buf) && (a:n < 0 || j < a:n) @@ -3874,17 +4690,17 @@ function! s:StringReader.getn(n) return r endfunction -function! s:StringReader.peekline() +function! s:StringReader.peekline() abort return self.peekn(-1) endfunction -function! s:StringReader.readline() +function! s:StringReader.readline() abort let r = self.getn(-1) call self.get() return r endfunction -function! s:StringReader.getstr(begin, end) +function! s:StringReader.getstr(begin, end) abort let r = '' for i in range(a:begin.i, a:end.i - 1) if i >= len(self.buf) @@ -3899,16 +4715,16 @@ function! s:StringReader.getstr(begin, end) return r endfunction -function! s:StringReader.getpos() +function! s:StringReader.getpos() abort let [lnum, col, offset] = self.pos[self.i] return {'i': self.i, 'lnum': lnum, 'col': col, 'offset': offset} endfunction -function! s:StringReader.setpos(pos) +function! s:StringReader.setpos(pos) abort let self.i = a:pos.i endfunction -function! s:StringReader.read_alpha() +function! s:StringReader.read_alpha() abort let r = '' while s:isalpha(self.peekn(1)) let r .= self.getn(1) @@ -3916,7 +4732,7 @@ function! s:StringReader.read_alpha() return r endfunction -function! s:StringReader.read_alnum() +function! s:StringReader.read_alnum() abort let r = '' while s:isalnum(self.peekn(1)) let r .= self.getn(1) @@ -3924,7 +4740,7 @@ function! s:StringReader.read_alnum() return r endfunction -function! s:StringReader.read_digit() +function! s:StringReader.read_digit() abort let r = '' while s:isdigit(self.peekn(1)) let r .= self.getn(1) @@ -3932,7 +4748,7 @@ function! s:StringReader.read_digit() return r endfunction -function! s:StringReader.read_odigit() +function! s:StringReader.read_odigit() abort let r = '' while s:isodigit(self.peekn(1)) let r .= self.getn(1) @@ -3940,7 +4756,24 @@ function! s:StringReader.read_odigit() return r endfunction -function! s:StringReader.read_xdigit() +function! s:StringReader.read_blob() abort + let r = '' + while 1 + let s = self.peekn(2) + if s =~# '^[0-9A-Fa-f][0-9A-Fa-f]$' + let r .= self.getn(2) + elseif s =~# '^\.[0-9A-Fa-f]$' + let r .= self.getn(1) + elseif s =~# '^[0-9A-Fa-f][^0-9A-Fa-f]$' + throw s:Err('E973: Blob literal should have an even number of hex characters:' . s, self.getpos()) + else + break + endif + endwhile + return r +endfunction + +function! s:StringReader.read_xdigit() abort let r = '' while s:isxdigit(self.peekn(1)) let r .= self.getn(1) @@ -3948,16 +4781,24 @@ function! s:StringReader.read_xdigit() return r endfunction -function! s:StringReader.read_integer() +function! s:StringReader.read_bdigit() abort + let r = '' + while self.peekn(1) ==# '0' || self.peekn(1) ==# '1' + let r .= self.getn(1) + endwhile + return r +endfunction + +function! s:StringReader.read_integer() abort let r = '' let c = self.peekn(1) - if c == '-' || c == '+' + if c ==# '-' || c ==# '+' let r = self.getn(1) endif return r . self.read_digit() endfunction -function! s:StringReader.read_word() +function! s:StringReader.read_word() abort let r = '' while s:iswordc(self.peekn(1)) let r .= self.getn(1) @@ -3965,7 +4806,7 @@ function! s:StringReader.read_word() return r endfunction -function! s:StringReader.read_white() +function! s:StringReader.read_white() abort let r = '' while s:iswhite(self.peekn(1)) let r .= self.getn(1) @@ -3973,15 +4814,17 @@ function! s:StringReader.read_white() return r endfunction -function! s:StringReader.read_nonwhite() +function! s:StringReader.read_nonwhite() abort let r = '' - while !s:iswhite(self.peekn(1)) + let ch = self.peekn(1) + while !s:iswhite(ch) && ch !=# '' let r .= self.getn(1) + let ch = self.peekn(1) endwhile return r endfunction -function! s:StringReader.read_name() +function! s:StringReader.read_name() abort let r = '' while s:isnamec(self.peekn(1)) let r .= self.getn(1) @@ -3989,13 +4832,13 @@ function! s:StringReader.read_name() return r endfunction -function! s:StringReader.skip_white() +function! s:StringReader.skip_white() abort while s:iswhite(self.peekn(1)) call self.seek_cur(1) endwhile endfunction -function! s:StringReader.skip_white_and_colon() +function! s:StringReader.skip_white_and_colon() abort while s:TRUE let c = self.peekn(1) if !s:iswhite(c) && c !=# ':' @@ -4007,19 +4850,19 @@ endfunction let s:Compiler = {} -function! s:Compiler.new(...) +function! s:Compiler.new(...) abort let obj = copy(self) call call(obj.__init__, a:000, obj) return obj endfunction -function! s:Compiler.__init__() +function! s:Compiler.__init__() abort let self.indent = [''] let self.lines = [] endfunction -function! s:Compiler.out(...) - if len(a:000) == 1 +function! s:Compiler.out(...) abort + if len(a:000) ==# 1 if a:000[0][0] ==# ')' let self.lines[-1] .= a:000[0] else @@ -4030,203 +4873,215 @@ function! s:Compiler.out(...) endif endfunction -function! s:Compiler.incindent(s) +function! s:Compiler.incindent(s) abort call insert(self.indent, self.indent[0] . a:s) endfunction -function! s:Compiler.decindent() +function! s:Compiler.decindent() abort call remove(self.indent, 0) endfunction -function! s:Compiler.compile(node) - if a:node.type == s:NODE_TOPLEVEL +function! s:Compiler.compile(node) abort + if a:node.type ==# s:NODE_TOPLEVEL return self.compile_toplevel(a:node) - elseif a:node.type == s:NODE_COMMENT + elseif a:node.type ==# s:NODE_COMMENT call self.compile_comment(a:node) return s:NIL - elseif a:node.type == s:NODE_EXCMD + elseif a:node.type ==# s:NODE_EXCMD call self.compile_excmd(a:node) return s:NIL - elseif a:node.type == s:NODE_FUNCTION + elseif a:node.type ==# s:NODE_FUNCTION call self.compile_function(a:node) return s:NIL - elseif a:node.type == s:NODE_DELFUNCTION + elseif a:node.type ==# s:NODE_DELFUNCTION call self.compile_delfunction(a:node) return s:NIL - elseif a:node.type == s:NODE_RETURN + elseif a:node.type ==# s:NODE_RETURN call self.compile_return(a:node) return s:NIL - elseif a:node.type == s:NODE_EXCALL + elseif a:node.type ==# s:NODE_EXCALL call self.compile_excall(a:node) return s:NIL - elseif a:node.type == s:NODE_LET + elseif a:node.type ==# s:NODE_EVAL + call self.compile_eval(a:node) + return s:NIL + elseif a:node.type ==# s:NODE_LET call self.compile_let(a:node) return s:NIL - elseif a:node.type == s:NODE_UNLET + elseif a:node.type ==# s:NODE_CONST + call self.compile_const(a:node) + return s:NIL + elseif a:node.type ==# s:NODE_UNLET call self.compile_unlet(a:node) return s:NIL - elseif a:node.type == s:NODE_LOCKVAR + elseif a:node.type ==# s:NODE_LOCKVAR call self.compile_lockvar(a:node) return s:NIL - elseif a:node.type == s:NODE_UNLOCKVAR + elseif a:node.type ==# s:NODE_UNLOCKVAR call self.compile_unlockvar(a:node) return s:NIL - elseif a:node.type == s:NODE_IF + elseif a:node.type ==# s:NODE_IF call self.compile_if(a:node) return s:NIL - elseif a:node.type == s:NODE_WHILE + elseif a:node.type ==# s:NODE_WHILE call self.compile_while(a:node) return s:NIL - elseif a:node.type == s:NODE_FOR + elseif a:node.type ==# s:NODE_FOR call self.compile_for(a:node) return s:NIL - elseif a:node.type == s:NODE_CONTINUE + elseif a:node.type ==# s:NODE_CONTINUE call self.compile_continue(a:node) return s:NIL - elseif a:node.type == s:NODE_BREAK + elseif a:node.type ==# s:NODE_BREAK call self.compile_break(a:node) return s:NIL - elseif a:node.type == s:NODE_TRY + elseif a:node.type ==# s:NODE_TRY call self.compile_try(a:node) return s:NIL - elseif a:node.type == s:NODE_THROW + elseif a:node.type ==# s:NODE_THROW call self.compile_throw(a:node) return s:NIL - elseif a:node.type == s:NODE_ECHO + elseif a:node.type ==# s:NODE_ECHO call self.compile_echo(a:node) return s:NIL - elseif a:node.type == s:NODE_ECHON + elseif a:node.type ==# s:NODE_ECHON call self.compile_echon(a:node) return s:NIL - elseif a:node.type == s:NODE_ECHOHL + elseif a:node.type ==# s:NODE_ECHOHL call self.compile_echohl(a:node) return s:NIL - elseif a:node.type == s:NODE_ECHOMSG + elseif a:node.type ==# s:NODE_ECHOMSG call self.compile_echomsg(a:node) return s:NIL - elseif a:node.type == s:NODE_ECHOERR + elseif a:node.type ==# s:NODE_ECHOERR call self.compile_echoerr(a:node) return s:NIL - elseif a:node.type == s:NODE_EXECUTE + elseif a:node.type ==# s:NODE_EXECUTE call self.compile_execute(a:node) return s:NIL - elseif a:node.type == s:NODE_TERNARY + elseif a:node.type ==# s:NODE_TERNARY return self.compile_ternary(a:node) - elseif a:node.type == s:NODE_OR + elseif a:node.type ==# s:NODE_OR return self.compile_or(a:node) - elseif a:node.type == s:NODE_AND + elseif a:node.type ==# s:NODE_AND return self.compile_and(a:node) - elseif a:node.type == s:NODE_EQUAL + elseif a:node.type ==# s:NODE_EQUAL return self.compile_equal(a:node) - elseif a:node.type == s:NODE_EQUALCI + elseif a:node.type ==# s:NODE_EQUALCI return self.compile_equalci(a:node) - elseif a:node.type == s:NODE_EQUALCS + elseif a:node.type ==# s:NODE_EQUALCS return self.compile_equalcs(a:node) - elseif a:node.type == s:NODE_NEQUAL + elseif a:node.type ==# s:NODE_NEQUAL return self.compile_nequal(a:node) - elseif a:node.type == s:NODE_NEQUALCI + elseif a:node.type ==# s:NODE_NEQUALCI return self.compile_nequalci(a:node) - elseif a:node.type == s:NODE_NEQUALCS + elseif a:node.type ==# s:NODE_NEQUALCS return self.compile_nequalcs(a:node) - elseif a:node.type == s:NODE_GREATER + elseif a:node.type ==# s:NODE_GREATER return self.compile_greater(a:node) - elseif a:node.type == s:NODE_GREATERCI + elseif a:node.type ==# s:NODE_GREATERCI return self.compile_greaterci(a:node) - elseif a:node.type == s:NODE_GREATERCS + elseif a:node.type ==# s:NODE_GREATERCS return self.compile_greatercs(a:node) - elseif a:node.type == s:NODE_GEQUAL + elseif a:node.type ==# s:NODE_GEQUAL return self.compile_gequal(a:node) - elseif a:node.type == s:NODE_GEQUALCI + elseif a:node.type ==# s:NODE_GEQUALCI return self.compile_gequalci(a:node) - elseif a:node.type == s:NODE_GEQUALCS + elseif a:node.type ==# s:NODE_GEQUALCS return self.compile_gequalcs(a:node) - elseif a:node.type == s:NODE_SMALLER + elseif a:node.type ==# s:NODE_SMALLER return self.compile_smaller(a:node) - elseif a:node.type == s:NODE_SMALLERCI + elseif a:node.type ==# s:NODE_SMALLERCI return self.compile_smallerci(a:node) - elseif a:node.type == s:NODE_SMALLERCS + elseif a:node.type ==# s:NODE_SMALLERCS return self.compile_smallercs(a:node) - elseif a:node.type == s:NODE_SEQUAL + elseif a:node.type ==# s:NODE_SEQUAL return self.compile_sequal(a:node) - elseif a:node.type == s:NODE_SEQUALCI + elseif a:node.type ==# s:NODE_SEQUALCI return self.compile_sequalci(a:node) - elseif a:node.type == s:NODE_SEQUALCS + elseif a:node.type ==# s:NODE_SEQUALCS return self.compile_sequalcs(a:node) - elseif a:node.type == s:NODE_MATCH + elseif a:node.type ==# s:NODE_MATCH return self.compile_match(a:node) - elseif a:node.type == s:NODE_MATCHCI + elseif a:node.type ==# s:NODE_MATCHCI return self.compile_matchci(a:node) - elseif a:node.type == s:NODE_MATCHCS + elseif a:node.type ==# s:NODE_MATCHCS return self.compile_matchcs(a:node) - elseif a:node.type == s:NODE_NOMATCH + elseif a:node.type ==# s:NODE_NOMATCH return self.compile_nomatch(a:node) - elseif a:node.type == s:NODE_NOMATCHCI + elseif a:node.type ==# s:NODE_NOMATCHCI return self.compile_nomatchci(a:node) - elseif a:node.type == s:NODE_NOMATCHCS + elseif a:node.type ==# s:NODE_NOMATCHCS return self.compile_nomatchcs(a:node) - elseif a:node.type == s:NODE_IS + elseif a:node.type ==# s:NODE_IS return self.compile_is(a:node) - elseif a:node.type == s:NODE_ISCI + elseif a:node.type ==# s:NODE_ISCI return self.compile_isci(a:node) - elseif a:node.type == s:NODE_ISCS + elseif a:node.type ==# s:NODE_ISCS return self.compile_iscs(a:node) - elseif a:node.type == s:NODE_ISNOT + elseif a:node.type ==# s:NODE_ISNOT return self.compile_isnot(a:node) - elseif a:node.type == s:NODE_ISNOTCI + elseif a:node.type ==# s:NODE_ISNOTCI return self.compile_isnotci(a:node) - elseif a:node.type == s:NODE_ISNOTCS + elseif a:node.type ==# s:NODE_ISNOTCS return self.compile_isnotcs(a:node) - elseif a:node.type == s:NODE_ADD + elseif a:node.type ==# s:NODE_ADD return self.compile_add(a:node) - elseif a:node.type == s:NODE_SUBTRACT + elseif a:node.type ==# s:NODE_SUBTRACT return self.compile_subtract(a:node) - elseif a:node.type == s:NODE_CONCAT + elseif a:node.type ==# s:NODE_CONCAT return self.compile_concat(a:node) - elseif a:node.type == s:NODE_MULTIPLY + elseif a:node.type ==# s:NODE_MULTIPLY return self.compile_multiply(a:node) - elseif a:node.type == s:NODE_DIVIDE + elseif a:node.type ==# s:NODE_DIVIDE return self.compile_divide(a:node) - elseif a:node.type == s:NODE_REMAINDER + elseif a:node.type ==# s:NODE_REMAINDER return self.compile_remainder(a:node) - elseif a:node.type == s:NODE_NOT + elseif a:node.type ==# s:NODE_NOT return self.compile_not(a:node) - elseif a:node.type == s:NODE_PLUS + elseif a:node.type ==# s:NODE_PLUS return self.compile_plus(a:node) - elseif a:node.type == s:NODE_MINUS + elseif a:node.type ==# s:NODE_MINUS return self.compile_minus(a:node) - elseif a:node.type == s:NODE_SUBSCRIPT + elseif a:node.type ==# s:NODE_SUBSCRIPT return self.compile_subscript(a:node) - elseif a:node.type == s:NODE_SLICE + elseif a:node.type ==# s:NODE_SLICE return self.compile_slice(a:node) - elseif a:node.type == s:NODE_DOT + elseif a:node.type ==# s:NODE_DOT return self.compile_dot(a:node) - elseif a:node.type == s:NODE_CALL + elseif a:node.type ==# s:NODE_METHOD + return self.compile_method(a:node) + elseif a:node.type ==# s:NODE_CALL return self.compile_call(a:node) - elseif a:node.type == s:NODE_NUMBER + elseif a:node.type ==# s:NODE_NUMBER return self.compile_number(a:node) - elseif a:node.type == s:NODE_STRING + elseif a:node.type ==# s:NODE_BLOB + return self.compile_blob(a:node) + elseif a:node.type ==# s:NODE_STRING return self.compile_string(a:node) - elseif a:node.type == s:NODE_LIST + elseif a:node.type ==# s:NODE_LIST return self.compile_list(a:node) - elseif a:node.type == s:NODE_DICT + elseif a:node.type ==# s:NODE_DICT return self.compile_dict(a:node) - elseif a:node.type == s:NODE_OPTION + elseif a:node.type ==# s:NODE_OPTION return self.compile_option(a:node) - elseif a:node.type == s:NODE_IDENTIFIER + elseif a:node.type ==# s:NODE_IDENTIFIER return self.compile_identifier(a:node) - elseif a:node.type == s:NODE_CURLYNAME + elseif a:node.type ==# s:NODE_CURLYNAME return self.compile_curlyname(a:node) - elseif a:node.type == s:NODE_ENV + elseif a:node.type ==# s:NODE_ENV return self.compile_env(a:node) - elseif a:node.type == s:NODE_REG + elseif a:node.type ==# s:NODE_REG return self.compile_reg(a:node) - elseif a:node.type == s:NODE_CURLYNAMEPART + elseif a:node.type ==# s:NODE_CURLYNAMEPART return self.compile_curlynamepart(a:node) - elseif a:node.type == s:NODE_CURLYNAMEEXPR + elseif a:node.type ==# s:NODE_CURLYNAMEEXPR return self.compile_curlynameexpr(a:node) - elseif a:node.type == s:NODE_LAMBDA + elseif a:node.type ==# s:NODE_LAMBDA return self.compile_lambda(a:node) - elseif a:node.type == s:NODE_PARENEXPR + elseif a:node.type ==# s:NODE_HEREDOC + return self.compile_heredoc(a:node) + elseif a:node.type ==# s:NODE_PARENEXPR return self.compile_parenexpr(a:node) else throw printf('Compiler: unknown node: %s', string(a:node)) @@ -4234,65 +5089,80 @@ function! s:Compiler.compile(node) return s:NIL endfunction -function! s:Compiler.compile_body(body) +function! s:Compiler.compile_body(body) abort for node in a:body call self.compile(node) endfor endfunction -function! s:Compiler.compile_toplevel(node) +function! s:Compiler.compile_toplevel(node) abort call self.compile_body(a:node.body) return self.lines endfunction -function! s:Compiler.compile_comment(node) +function! s:Compiler.compile_comment(node) abort call self.out(';%s', a:node.str) endfunction -function! s:Compiler.compile_excmd(node) +function! s:Compiler.compile_excmd(node) abort call self.out('(excmd "%s")', escape(a:node.str, '\"')) endfunction -function! s:Compiler.compile_function(node) +function! s:Compiler.compile_function(node) abort let left = self.compile(a:node.left) let rlist = map(a:node.rlist, 'self.compile(v:val)') - if !empty(rlist) && rlist[-1] ==# '...' - let rlist[-1] = '. ...' - endif - if empty(rlist) - call self.out('(function (%s)', left) - else - call self.out('(function (%s %s)', left, join(rlist, ' ')) + let default_args = map(a:node.default_args, 'self.compile(v:val)') + if !empty(rlist) + let remaining = s:FALSE + if rlist[-1] ==# '...' + call remove(rlist, -1) + let remaining = s:TRUE + endif + for i in range(len(rlist)) + if i < len(rlist) - len(default_args) + let left .= printf(' %s', rlist[i]) + else + let left .= printf(' (%s %s)', rlist[i], default_args[i + len(default_args) - len(rlist)]) + endif + endfor + if remaining + let left .= ' . ...' + endif endif + call self.out('(function (%s)', left) call self.incindent(' ') call self.compile_body(a:node.body) call self.out(')') call self.decindent() endfunction -function! s:Compiler.compile_delfunction(node) +function! s:Compiler.compile_delfunction(node) abort call self.out('(delfunction %s)', self.compile(a:node.left)) endfunction -function! s:Compiler.compile_return(node) - if a:node.left is s:NIL +function! s:Compiler.compile_return(node) abort + if a:node.left is# s:NIL call self.out('(return)') else call self.out('(return %s)', self.compile(a:node.left)) endif endfunction -function! s:Compiler.compile_excall(node) +function! s:Compiler.compile_excall(node) abort call self.out('(call %s)', self.compile(a:node.left)) endfunction -function! s:Compiler.compile_let(node) +function! s:Compiler.compile_eval(node) abort + call self.out('(eval %s)', self.compile(a:node.left)) +endfunction + +function! s:Compiler.compile_let(node) abort let left = '' - if a:node.left isnot s:NIL + if a:node.left isnot# s:NIL let left = self.compile(a:node.left) else let left = join(map(a:node.list, 'self.compile(v:val)'), ' ') - if a:node.rest isnot s:NIL + if a:node.rest isnot# s:NIL let left .= ' . ' . self.compile(a:node.rest) endif let left = '(' . left . ')' @@ -4301,30 +5171,46 @@ function! s:Compiler.compile_let(node) call self.out('(let %s %s %s)', a:node.op, left, right) endfunction -function! s:Compiler.compile_unlet(node) +" TODO: merge with s:Compiler.compile_let() ? +function! s:Compiler.compile_const(node) abort + let left = '' + if a:node.left isnot# s:NIL + let left = self.compile(a:node.left) + else + let left = join(map(a:node.list, 'self.compile(v:val)'), ' ') + if a:node.rest isnot# s:NIL + let left .= ' . ' . self.compile(a:node.rest) + endif + let left = '(' . left . ')' + endif + let right = self.compile(a:node.right) + call self.out('(const %s %s %s)', a:node.op, left, right) +endfunction + +function! s:Compiler.compile_unlet(node) abort let list = map(a:node.list, 'self.compile(v:val)') call self.out('(unlet %s)', join(list, ' ')) endfunction -function! s:Compiler.compile_lockvar(node) +function! s:Compiler.compile_lockvar(node) abort let list = map(a:node.list, 'self.compile(v:val)') - if a:node.depth is s:NIL + if a:node.depth is# s:NIL call self.out('(lockvar %s)', join(list, ' ')) else call self.out('(lockvar %d %s)', a:node.depth, join(list, ' ')) endif endfunction -function! s:Compiler.compile_unlockvar(node) +function! s:Compiler.compile_unlockvar(node) abort let list = map(a:node.list, 'self.compile(v:val)') - if a:node.depth is s:NIL + if a:node.depth is# s:NIL call self.out('(unlockvar %s)', join(list, ' ')) else call self.out('(unlockvar %d %s)', a:node.depth, join(list, ' ')) endif endfunction -function! s:Compiler.compile_if(node) +function! s:Compiler.compile_if(node) abort call self.out('(if %s', self.compile(a:node.cond)) call self.incindent(' ') call self.compile_body(a:node.body) @@ -4335,7 +5221,7 @@ function! s:Compiler.compile_if(node) call self.compile_body(enode.body) call self.decindent() endfor - if a:node.else isnot s:NIL + if a:node.else isnot# s:NIL call self.out(' else') call self.incindent(' ') call self.compile_body(a:node.else.body) @@ -4346,7 +5232,7 @@ function! s:Compiler.compile_if(node) call self.decindent() endfunction -function! s:Compiler.compile_while(node) +function! s:Compiler.compile_while(node) abort call self.out('(while %s', self.compile(a:node.cond)) call self.incindent(' ') call self.compile_body(a:node.body) @@ -4354,13 +5240,13 @@ function! s:Compiler.compile_while(node) call self.decindent() endfunction -function! s:Compiler.compile_for(node) +function! s:Compiler.compile_for(node) abort let left = '' - if a:node.left isnot s:NIL + if a:node.left isnot# s:NIL let left = self.compile(a:node.left) else let left = join(map(a:node.list, 'self.compile(v:val)'), ' ') - if a:node.rest isnot s:NIL + if a:node.rest isnot# s:NIL let left .= ' . ' . self.compile(a:node.rest) endif let left = '(' . left . ')' @@ -4373,20 +5259,20 @@ function! s:Compiler.compile_for(node) call self.decindent() endfunction -function! s:Compiler.compile_continue(node) +function! s:Compiler.compile_continue(node) abort call self.out('(continue)') endfunction -function! s:Compiler.compile_break(node) +function! s:Compiler.compile_break(node) abort call self.out('(break)') endfunction -function! s:Compiler.compile_try(node) +function! s:Compiler.compile_try(node) abort call self.out('(try') call self.incindent(' ') call self.compile_body(a:node.body) for cnode in a:node.catch - if cnode.pattern isnot s:NIL + if cnode.pattern isnot# s:NIL call self.decindent() call self.out(' catch /%s/', cnode.pattern) call self.incindent(' ') @@ -4398,7 +5284,7 @@ function! s:Compiler.compile_try(node) call self.compile_body(cnode.body) endif endfor - if a:node.finally isnot s:NIL + if a:node.finally isnot# s:NIL call self.decindent() call self.out(' finally') call self.incindent(' ') @@ -4408,222 +5294,226 @@ function! s:Compiler.compile_try(node) call self.decindent() endfunction -function! s:Compiler.compile_throw(node) +function! s:Compiler.compile_throw(node) abort call self.out('(throw %s)', self.compile(a:node.left)) endfunction -function! s:Compiler.compile_echo(node) +function! s:Compiler.compile_echo(node) abort let list = map(a:node.list, 'self.compile(v:val)') call self.out('(echo %s)', join(list, ' ')) endfunction -function! s:Compiler.compile_echon(node) +function! s:Compiler.compile_echon(node) abort let list = map(a:node.list, 'self.compile(v:val)') call self.out('(echon %s)', join(list, ' ')) endfunction -function! s:Compiler.compile_echohl(node) +function! s:Compiler.compile_echohl(node) abort call self.out('(echohl "%s")', escape(a:node.str, '\"')) endfunction -function! s:Compiler.compile_echomsg(node) +function! s:Compiler.compile_echomsg(node) abort let list = map(a:node.list, 'self.compile(v:val)') call self.out('(echomsg %s)', join(list, ' ')) endfunction -function! s:Compiler.compile_echoerr(node) +function! s:Compiler.compile_echoerr(node) abort let list = map(a:node.list, 'self.compile(v:val)') call self.out('(echoerr %s)', join(list, ' ')) endfunction -function! s:Compiler.compile_execute(node) +function! s:Compiler.compile_execute(node) abort let list = map(a:node.list, 'self.compile(v:val)') call self.out('(execute %s)', join(list, ' ')) endfunction -function! s:Compiler.compile_ternary(node) +function! s:Compiler.compile_ternary(node) abort return printf('(?: %s %s %s)', self.compile(a:node.cond), self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_or(node) +function! s:Compiler.compile_or(node) abort return printf('(|| %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_and(node) +function! s:Compiler.compile_and(node) abort return printf('(&& %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_equal(node) +function! s:Compiler.compile_equal(node) abort return printf('(== %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_equalci(node) +function! s:Compiler.compile_equalci(node) abort return printf('(==? %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_equalcs(node) +function! s:Compiler.compile_equalcs(node) abort return printf('(==# %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_nequal(node) +function! s:Compiler.compile_nequal(node) abort return printf('(!= %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_nequalci(node) +function! s:Compiler.compile_nequalci(node) abort return printf('(!=? %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_nequalcs(node) +function! s:Compiler.compile_nequalcs(node) abort return printf('(!=# %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_greater(node) +function! s:Compiler.compile_greater(node) abort return printf('(> %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_greaterci(node) +function! s:Compiler.compile_greaterci(node) abort return printf('(>? %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_greatercs(node) +function! s:Compiler.compile_greatercs(node) abort return printf('(># %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_gequal(node) +function! s:Compiler.compile_gequal(node) abort return printf('(>= %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_gequalci(node) +function! s:Compiler.compile_gequalci(node) abort return printf('(>=? %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_gequalcs(node) +function! s:Compiler.compile_gequalcs(node) abort return printf('(>=# %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_smaller(node) +function! s:Compiler.compile_smaller(node) abort return printf('(< %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction -function! s:Compiler.compile_smallerci(node) +function! s:Compiler.compile_smallerci(node) abort return printf('(' || a:c ==# '' || a:c ==# self.delim endfunction -function! s:RegexpParser.parse_regexp() +function! s:RegexpParser.parse_regexp() abort let prevtoken = '' let ntoken = '' let ret = [] @@ -4750,13 +5670,13 @@ function! s:RegexpParser.parse_regexp() endif elseif ntoken ==# '\^' " '^' is only magic as the very first character. - if self.reg_magic != self.RE_VERY_MAGIC && prevtoken !=# '' && prevtoken !=# '\&' && prevtoken !=# '\|' && prevtoken !=# '\n' && prevtoken !=# '\(' && prevtoken !=# '\%(' + if self.reg_magic !=# self.RE_VERY_MAGIC && prevtoken !=# '' && prevtoken !=# '\&' && prevtoken !=# '\|' && prevtoken !=# '\n' && prevtoken !=# '\(' && prevtoken !=# '\%(' let ntoken = '^' endif elseif ntoken ==# '\$' " '$' is only magic as the very last character let pos = self.reader.tell() - if self.reg_magic != self.RE_VERY_MAGIC + if self.reg_magic !=# self.RE_VERY_MAGIC while !self.isend(self.reader.peek()) let [t, n] = self.get_token() " XXX: Vim doesn't check \v and \V? @@ -4782,19 +5702,19 @@ function! s:RegexpParser.parse_regexp() endfunction " @return [actual_token, normalized_token] -function! s:RegexpParser.get_token() - if self.reg_magic == self.RE_VERY_MAGIC +function! s:RegexpParser.get_token() abort + if self.reg_magic ==# self.RE_VERY_MAGIC return self.get_token_very_magic() - elseif self.reg_magic == self.RE_MAGIC + elseif self.reg_magic ==# self.RE_MAGIC return self.get_token_magic() - elseif self.reg_magic == self.RE_NOMAGIC + elseif self.reg_magic ==# self.RE_NOMAGIC return self.get_token_nomagic() - elseif self.reg_magic == self.RE_VERY_NOMAGIC + elseif self.reg_magic ==# self.RE_VERY_NOMAGIC return self.get_token_very_nomagic() endif endfunction -function! s:RegexpParser.get_token_very_magic() +function! s:RegexpParser.get_token_very_magic() abort if self.isend(self.reader.peek()) return ['', ''] endif @@ -4841,7 +5761,7 @@ function! s:RegexpParser.get_token_very_magic() return [c, c] endfunction -function! s:RegexpParser.get_token_magic() +function! s:RegexpParser.get_token_magic() abort if self.isend(self.reader.peek()) return ['', ''] endif @@ -4892,7 +5812,7 @@ function! s:RegexpParser.get_token_magic() return [c, c] endfunction -function! s:RegexpParser.get_token_nomagic() +function! s:RegexpParser.get_token_nomagic() abort if self.isend(self.reader.peek()) return ['', ''] endif @@ -4943,7 +5863,7 @@ function! s:RegexpParser.get_token_nomagic() return [c, c] endfunction -function! s:RegexpParser.get_token_very_nomagic() +function! s:RegexpParser.get_token_very_nomagic() abort if self.isend(self.reader.peek()) return ['', ''] endif @@ -4992,17 +5912,17 @@ function! s:RegexpParser.get_token_very_nomagic() return [c, c] endfunction -function! s:RegexpParser.get_token_backslash_common() +function! s:RegexpParser.get_token_backslash_common() abort let cclass = 'iIkKfFpPsSdDxXoOwWhHaAlLuU' let c = self.reader.get() if c ==# '\' return ['\\', '\\'] - elseif stridx(cclass, c) != -1 + elseif stridx(cclass, c) !=# -1 return ['\' . c, '\' . c] - elseif c == '_' + elseif c ==# '_' let epos = self.reader.getpos() let c = self.reader.get() - if stridx(cclass, c) != -1 + if stridx(cclass, c) !=# -1 return ['\_' . c, '\_ . c'] elseif c ==# '^' return ['\_^', '\_^'] @@ -5014,14 +5934,14 @@ function! s:RegexpParser.get_token_backslash_common() return self.get_token_sq('\_[') endif throw s:Err('E63: invalid use of \_', epos) - elseif stridx('etrb', c) != -1 + elseif stridx('etrb', c) !=# -1 return ['\' . c, '\' . c] - elseif stridx('123456789', c) != -1 + elseif stridx('123456789', c) !=# -1 return ['\' . c, '\' . c] - elseif c == 'z' + elseif c ==# 'z' let epos = self.reader.getpos() let c = self.reader.get() - if stridx('123456789', c) != -1 + if stridx('123456789', c) !=# -1 return ['\z' . c, '\z' . c] elseif c ==# 's' return ['\zs', '\zs'] @@ -5031,9 +5951,9 @@ function! s:RegexpParser.get_token_backslash_common() return ['\z(', '\z('] endif throw s:Err('E68: Invalid character after \z', epos) - elseif stridx('cCmMvVZ', c) != -1 + elseif stridx('cCmMvVZ', c) !=# -1 return ['\' . c, '\' . c] - elseif c == '%' + elseif c ==# '%' let epos = self.reader.getpos() let c = self.reader.get() if c ==# 'd' @@ -5068,7 +5988,7 @@ function! s:RegexpParser.get_token_backslash_common() endfunction " \{} -function! s:RegexpParser.get_token_brace(pre) +function! s:RegexpParser.get_token_brace(pre) abort let r = '' let minus = '' let comma = '' @@ -5101,7 +6021,7 @@ function! s:RegexpParser.get_token_brace(pre) endfunction " \[] -function! s:RegexpParser.get_token_sq(pre) +function! s:RegexpParser.get_token_sq(pre) abort let start = self.reader.tell() let r = '' " Complement of range @@ -5138,7 +6058,7 @@ function! s:RegexpParser.get_token_sq(pre) let [e, startc] = self.get_token_sq_c() let r .= e endif - if startc != 0 && self.reader.p(0) ==# '-' && !self.isend(self.reader.p(1)) && !(self.reader.p(1) ==# '\' && self.reader.p(2) ==# 'n') + if startc !=# 0 && self.reader.p(0) ==# '-' && !self.isend(self.reader.p(1)) && !(self.reader.p(1) ==# '\' && self.reader.p(2) ==# 'n') call self.reader.seek_cur(1) let r .= '-' let c = self.reader.p(0) @@ -5162,7 +6082,7 @@ function! s:RegexpParser.get_token_sq(pre) endfunction " [c] -function! s:RegexpParser.get_token_sq_c() +function! s:RegexpParser.get_token_sq_c() abort let c = self.reader.p(0) if c ==# '\' call self.reader.seek_cur(1) @@ -5172,20 +6092,20 @@ function! s:RegexpParser.get_token_sq_c() return ['\n', 0] elseif c ==# 'r' call self.reader.seek_cur(1) - return ['\r', char2nr("\r")] + return ['\r', 13] elseif c ==# 't' call self.reader.seek_cur(1) - return ['\t', char2nr("\t")] + return ['\t', 9] elseif c ==# 'e' call self.reader.seek_cur(1) - return ['\e', char2nr("\e")] + return ['\e', 27] elseif c ==# 'b' call self.reader.seek_cur(1) - return ['\b', char2nr("\b")] - elseif stridx(']^-\', c) != -1 + return ['\b', 8] + elseif stridx(']^-\', c) !=# -1 call self.reader.seek_cur(1) return ['\' . c, char2nr(c)] - elseif stridx('doxuU', c) != -1 + elseif stridx('doxuU', c) !=# -1 let [c, n] = self.get_token_sq_coll_char() return [c, n] else @@ -5201,7 +6121,7 @@ function! s:RegexpParser.get_token_sq_c() endfunction " [\d123] -function! s:RegexpParser.get_token_sq_coll_char() +function! s:RegexpParser.get_token_sq_coll_char() abort let pos = self.reader.tell() let c = self.reader.get() if c ==# 'd' @@ -5230,7 +6150,7 @@ function! s:RegexpParser.get_token_sq_coll_char() endfunction " [[.a.]] -function! s:RegexpParser.get_token_sq_coll_element() +function! s:RegexpParser.get_token_sq_coll_element() abort if self.reader.p(0) ==# '[' && self.reader.p(1) ==# '.' && !self.isend(self.reader.p(2)) && self.reader.p(3) ==# '.' && self.reader.p(4) ==# ']' return self.reader.getn(5) endif @@ -5238,7 +6158,7 @@ function! s:RegexpParser.get_token_sq_coll_element() endfunction " [[=a=]] -function! s:RegexpParser.get_token_sq_equi_class() +function! s:RegexpParser.get_token_sq_equi_class() abort if self.reader.p(0) ==# '[' && self.reader.p(1) ==# '=' && !self.isend(self.reader.p(2)) && self.reader.p(3) ==# '=' && self.reader.p(4) ==# ']' return self.reader.getn(5) endif @@ -5246,8 +6166,8 @@ function! s:RegexpParser.get_token_sq_equi_class() endfunction " [[:alpha:]] -function! s:RegexpParser.get_token_sq_char_class() - let class_names = ["alnum", "alpha", "blank", "cntrl", "digit", "graph", "lower", "print", "punct", "space", "upper", "xdigit", "tab", "return", "backspace", "escape"] +function! s:RegexpParser.get_token_sq_char_class() abort + let class_names = ['alnum', 'alpha', 'blank', 'cntrl', 'digit', 'graph', 'lower', 'print', 'punct', 'space', 'upper', 'xdigit', 'tab', 'return', 'backspace', 'escape'] let pos = self.reader.tell() if self.reader.p(0) ==# '[' && self.reader.p(1) ==# ':' call self.reader.seek_cur(2) @@ -5266,7 +6186,7 @@ function! s:RegexpParser.get_token_sq_char_class() endfunction " \@... -function! s:RegexpParser.get_token_at(pre) +function! s:RegexpParser.get_token_at(pre) abort let epos = self.reader.getpos() let c = self.reader.get() if c ==# '>' @@ -5287,7 +6207,7 @@ function! s:RegexpParser.get_token_at(pre) endfunction " \%... -function! s:RegexpParser.get_token_percent(pre) +function! s:RegexpParser.get_token_percent(pre) abort let c = self.reader.get() if c ==# '^' return [a:pre . '^', '\%^'] @@ -5307,7 +6227,7 @@ function! s:RegexpParser.get_token_percent(pre) endfunction " \%[] -function! s:RegexpParser.get_token_percent_sq(pre) +function! s:RegexpParser.get_token_percent_sq(pre) abort let r = '' while s:TRUE let c = self.reader.peek() @@ -5327,7 +6247,7 @@ function! s:RegexpParser.get_token_percent_sq(pre) endfunction " \%'m \%l \%c \%v -function! s:RegexpParser.get_token_mlvc(pre) +function! s:RegexpParser.get_token_mlvc(pre) abort let r = '' let cmp = '' if self.reader.p(0) ==# '<' || self.reader.p(0) ==# '>' @@ -5362,15 +6282,15 @@ function! s:RegexpParser.get_token_mlvc(pre) throw s:Err('E71: Invalid character after %', self.reader.getpos()) endfunction -function! s:RegexpParser.getdecchrs() +function! s:RegexpParser.getdecchrs() abort return self.reader.read_digit() endfunction -function! s:RegexpParser.getoctchrs() +function! s:RegexpParser.getoctchrs() abort return self.reader.read_odigit() endfunction -function! s:RegexpParser.gethexchrs(n) +function! s:RegexpParser.gethexchrs(n) abort let r = '' for i in range(a:n) let c = self.reader.peek() diff --git a/compiler/compiler.go b/compiler/compiler.go index 8763f05..3726bc5 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -129,6 +129,8 @@ func (c *Compiler) compileExcommand(node ast.ExCommand) error { c.compileTry(n) case *ast.Throw: c.compileThrow(n) + case *ast.Eval: + c.compileEval(n) case *ast.EchoCmd: c.compileEchocmd(n) case *ast.Echohl: @@ -151,13 +153,22 @@ func (c *Compiler) compileFunction(node *ast.Function) { if len(node.Params) > 0 { c.fprint(" ") ps := make([]string, 0, len(node.Params)) - for _, p := range node.Params { - if p.Name == token.DOTDOTDOT.String() { - ps = append(ps, ". ...") - } else { + var remaining bool + if node.Params[len(node.Params)-1].Name == token.DOTDOTDOT.String() { + node.Params = node.Params[:len(node.Params)-1] + remaining = true + } + for i, p := range node.Params { + if i < len(node.Params)-len(node.DefaultArgs) { ps = append(ps, p.Name) + } else { + ps = append(ps, fmt.Sprintf("(%s %s)", p.Name, + c.compileExpr(node.DefaultArgs[i+len(node.DefaultArgs)-len(node.Params)]))) } } + if remaining { + ps = append(ps, ". ...") + } c.fprint("%s", strings.Join(ps, " ")) } c.fprintln(")") @@ -332,6 +343,11 @@ func (c *Compiler) compileThrow(node *ast.Throw) { c.fprintln("(%s %s)", cmd, c.compileExpr(node.Expr)) } +func (c *Compiler) compileEval(node *ast.Eval) { + cmd := node.Cmd().Name + c.fprintln("(%s %s)", cmd, c.compileExpr(node.Expr)) +} + func (c *Compiler) compileEchocmd(node *ast.EchoCmd) { cmd := node.Cmd().Name exprs := make([]string, 0, len(node.Exprs)) @@ -387,6 +403,14 @@ func (c *Compiler) compileExpr(node ast.Expr) string { h = c.compileExpr(n.High) } return fmt.Sprintf("(slice %s %s %s)", x, l, h) + case *ast.MethodExpr: + name := c.compileExpr(n.Left) + args := make([]string, 0, len(n.Args)+1) + args = append(args, c.compileExpr(n.Method)) + for _, a := range n.Args { + args = append(args, c.compileExpr(a)) + } + return fmt.Sprintf("(method %s (%s))", name, strings.Join(args, " ")) case *ast.CallExpr: name := c.compileExpr(n.Fun) if len(n.Args) > 0 { @@ -440,6 +464,28 @@ func (c *Compiler) compileExpr(node ast.Expr) string { params = append(params, p.Name) } return fmt.Sprintf("(lambda (%s) %s)", strings.Join(params, " "), c.compileExpr(n.Expr)) + case *ast.HeredocExpr: + var flags string + if len(n.Flags) == 0 { + flags = "(list)" + } else { + xs := make([]string, len(n.Flags)) + for i, f := range n.Flags { + xs[i] = `"` + c.compileExpr(f) + `"` + } + flags = fmt.Sprintf("(list %s)", strings.Join(xs, " ")) + } + var body string + if len(n.Body) == 0 { + body = "(list)" + } else { + xs := make([]string, len(n.Body)) + for i, p := range n.Body { + xs[i] = `"` + escape(c.compileExpr(p), "\n\t\r") + `"` + } + body = fmt.Sprintf("(list %s)", strings.Join(xs, " ")) + } + return fmt.Sprintf(`(heredoc %s "%s" %s)`, flags, n.EndMarker, body) case *ast.ParenExpr: return c.compileExpr(n.X) } @@ -450,7 +496,16 @@ func escape(s string, chars string) string { r := "" for _, c := range s { if strings.IndexRune(chars, c) != -1 { - r += `\` + string(c) + switch c { + case '\n': + r += `\n` + case '\t': + r += `\t` + case '\r': + r += `\r` + default: + r += `\` + string(c) + } } else { r += string(c) } diff --git a/go/_test/special.go b/go/_test/special.go index df0d8cc..de05787 100644 --- a/go/_test/special.go +++ b/go/_test/special.go @@ -3,7 +3,8 @@ self.hoge = 1 self.ea.range_ = 1 xxx.x = 1 var z = self.ea.range_ -var xs = viml_range(10) +var xs = viml_range(0, 10 - 1) +xs = viml_range(0, 10) Node() var type_ = 1 var t = type_ @@ -34,6 +35,9 @@ node.attr.dict = true // skip // end skip // do not skip +node.rlist = []*VimNode{} +node.list = []*VimNode{} +node.depth = 0 node.list = self.parse_lvaluelist() node.depth = hoge node.rlist = []*VimNode{nil, nil} diff --git a/go/_test/special.vim b/go/_test/special.vim index 294a9cd..e5c0573 100644 --- a/go/_test/special.vim +++ b/go/_test/special.vim @@ -78,6 +78,7 @@ let self.ea.range = 1 let xxx.x = 1 let z = self.ea.range let xs = range(10) +let xs = range(0, 10) function! s:Node() " skip Node definition @@ -122,7 +123,6 @@ let self.context = {} let toplevel.body = {} let node.body = [] -let node.rlist = [] let node.attr = {'range': 0, 'abort': 0, 'dict': 0} let node.endfunction = s:NIL let node.endif = s:NIL @@ -133,14 +133,15 @@ let node.elseif = s:NIL let node.catch = [] let node.finally = [] -let node.list = [] -let node.depth = s:NIL let node.pattern = s:NIL let lhs.list = [] " end skip " do not skip +let node.rlist = [] +let node.list = [] +let node.depth = s:NIL let node.list = self.parse_lvaluelist() let node.depth = hoge let node.pattern = node diff --git a/go/_test/while.go b/go/_test/while.go index 398be29..853a9fa 100644 --- a/go/_test/while.go +++ b/go/_test/while.go @@ -3,3 +3,6 @@ for b { for { var x = 1 } +for { + var x = 1 +} diff --git a/go/_test/while.vim b/go/_test/while.vim index 015fd92..666ffca 100644 --- a/go/_test/while.vim +++ b/go/_test/while.vim @@ -3,3 +3,6 @@ endwhile while 1 let x = 1 endwhile +while 1 + let x = 1 +endwhile diff --git a/go/builtin_commands.go b/go/builtin_commands.go index 02bd2a8..9ba4b19 100644 --- a/go/builtin_commands.go +++ b/go/builtin_commands.go @@ -121,7 +121,7 @@ var builtin_commands = []*Cmd{ {flags: "NEEDARG|EXTRA|NOTRLCOM|SBOXOK|CMDWIN", minlen: 3, name: "debug", parser: "parse_cmd_common"}, {flags: "RANGE|NOTADR|ZEROR|TRLBAR|CMDWIN", minlen: 6, name: "debuggreedy", parser: "parse_cmd_common"}, {flags: "NEEDARG|WORD1|TRLBAR|CMDWIN", minlen: 4, name: "delcommand", parser: "parse_cmd_common"}, - {flags: "NEEDARG|WORD1|CMDWIN", minlen: 4, name: "delfunction", parser: "parse_cmd_delfunction"}, + {flags: "BANG|NEEDARG|WORD1|CMDWIN", minlen: 4, name: "delfunction", parser: "parse_cmd_delfunction"}, {flags: "BANG|TRLBAR", minlen: 3, name: "diffupdate", parser: "parse_cmd_common"}, {flags: "RANGE|EXTRA|TRLBAR|MODIFY", minlen: 5, name: "diffget", parser: "parse_cmd_common"}, {flags: "BANG|TRLBAR", minlen: 5, name: "diffoff", parser: "parse_cmd_common"}, @@ -154,6 +154,7 @@ var builtin_commands = []*Cmd{ {flags: "TRLBAR|SBOXOK|CMDWIN", minlen: 4, name: "endtry", parser: "parse_cmd_endtry"}, {flags: "TRLBAR|SBOXOK|CMDWIN", minlen: 4, name: "endwhile", parser: "parse_cmd_endwhile"}, {flags: "BANG|TRLBAR", minlen: 3, name: "enew", parser: "parse_cmd_common"}, + {flags: "EXTRA|NOTRLCOM|SBOXOK|CMDWIN", minlen: 2, name: "eval", parser: "parse_cmd_eval"}, {flags: "BANG|FILE1|EDITCMD|ARGOPT|TRLBAR", minlen: 2, name: "ex", parser: "parse_cmd_common"}, {flags: "EXTRA|NOTRLCOM|SBOXOK|CMDWIN", minlen: 3, name: "execute", parser: "parse_cmd_execute"}, {flags: "RANGE|WHOLEFOLD|BANG|FILE1|ARGOPT|DFLALL|TRLBAR|CMDWIN", minlen: 3, name: "exit", parser: "parse_cmd_common"}, @@ -229,6 +230,7 @@ var builtin_commands = []*Cmd{ {flags: "TRLBAR|RANGE|WHOLEFOLD|EXTRA|CMDWIN|MODIFY", minlen: 2, name: "left", parser: "parse_cmd_common"}, {flags: "NEEDARG|EXTRA|NOTRLCOM", minlen: 5, name: "leftabove", parser: "parse_cmd_common"}, {flags: "EXTRA|NOTRLCOM|SBOXOK|CMDWIN", minlen: 3, name: "let", parser: "parse_cmd_let"}, + {flags: "EXTRA|NOTRLCOM|SBOXOK|CMDWIN", minlen: 4, name: "const", parser: "parse_cmd_const"}, {flags: "NEEDARG|WORD1|NOTRLCOM|TRLBAR|BANG", minlen: 3, name: "lexpr", parser: "parse_cmd_common"}, {flags: "TRLBAR|FILE1|BANG", minlen: 2, name: "lfile", parser: "parse_cmd_common"}, {flags: "RANGE|NOTADR|COUNT|TRLBAR|BANG", minlen: 4, name: "lfirst", parser: "parse_cmd_common"}, @@ -564,4 +566,28 @@ var builtin_commands = []*Cmd{ {flags: "EXTRA|TRLBAR|CMDWIN", minlen: 5, name: "tmapclear", parser: "parse_cmd_common"}, {flags: "EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN", minlen: 3, name: "tnoremap", parser: "parse_cmd_common"}, {flags: "EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN", minlen: 5, name: "tunmap", parser: "parse_cmd_common"}, + {flags: "RANGE|COUNT|TRLBAR", minlen: 4, name: "cabove", parser: "parse_cmd_common"}, + {flags: "RANGE|COUNT|TRLBAR", minlen: 3, name: "cafter", parser: "parse_cmd_common"}, + {flags: "RANGE|COUNT|TRLBAR", minlen: 3, name: "cbefore", parser: "parse_cmd_common"}, + {flags: "RANGE|COUNT|TRLBAR", minlen: 4, name: "cbelow", parser: "parse_cmd_common"}, + {flags: "EXTRA|NOTRLCOM|SBOXOK|CMDWIN", minlen: 4, name: "const", parser: "parse_cmd_common"}, + {flags: "RANGE|COUNT|TRLBAR", minlen: 3, name: "labove", parser: "parse_cmd_common"}, + {flags: "RANGE|COUNT|TRLBAR", minlen: 3, name: "lafter", parser: "parse_cmd_common"}, + {flags: "RANGE|COUNT|TRLBAR", minlen: 3, name: "lbefore", parser: "parse_cmd_common"}, + {flags: "RANGE|COUNT|TRLBAR", minlen: 4, name: "lbelow", parser: "parse_cmd_common"}, + {flags: "TRLBAR|CMDWIN", minlen: 7, name: "redrawtabline", parser: "parse_cmd_common"}, + {flags: "WORD1|TRLBAR|CMDWIN", minlen: 7, name: "scriptversion", parser: "parse_cmd_common"}, + {flags: "BANG|FILE1|TRLBAR|CMDWIN", minlen: 2, name: "tcd", parser: "parse_cmd_common"}, + {flags: "BANG|FILE1|TRLBAR|CMDWIN", minlen: 3, name: "tchdir", parser: "parse_cmd_common"}, + {flags: "RANGE|ZEROR|EXTRA|TRLBAR|NOTRLCOM|CTRLV|CMDWIN", minlen: 3, name: "tlmenu", parser: "parse_cmd_common"}, + {flags: "RANGE|ZEROR|EXTRA|TRLBAR|NOTRLCOM|CTRLV|CMDWIN", minlen: 3, name: "tlnoremenu", parser: "parse_cmd_common"}, + {flags: "RANGE|ZEROR|EXTRA|TRLBAR|NOTRLCOM|CTRLV|CMDWIN", minlen: 3, name: "tlunmenu", parser: "parse_cmd_common"}, + {flags: "EXTRA|TRLBAR|CMDWIN", minlen: 2, name: "xrestore", parser: "parse_cmd_common"}, + {flags: "EXTRA|BANG|SBOXOK|CMDWIN", minlen: 3, name: "def", parser: "parse_cmd_common"}, + {flags: "EXTRA|NEEDARG|TRLBAR|CMDWIN", minlen: 4, name: "disassemble", parser: "parse_cmd_common"}, + {flags: "TRLBAR|CMDWIN", minlen: 4, name: "enddef", parser: "parse_cmd_common"}, + {flags: "EXTRA|NOTRLCOM", minlen: 3, name: "export", parser: "parse_cmd_common"}, + {flags: "EXTRA|NOTRLCOM", minlen: 3, name: "import", parser: "parse_cmd_common"}, + {flags: "BANG|RANGE|NEEDARG|EXTRA|TRLBAR", minlen: 7, name: "spellrare", parser: "parse_cmd_common"}, + {flags: "", minlen: 4, name: "vim9script", parser: "parse_cmd_common"}, } diff --git a/go/export.go b/go/export.go index 001ed23..dae8c2c 100644 --- a/go/export.go +++ b/go/export.go @@ -69,6 +69,7 @@ func newAstNode(n *VimNode, filename string) ast.Node { Body: newBody(*n, filename), Name: newExprNode(n.left, filename), Params: newIdents(*n, filename), + DefaultArgs: newExprs(n.default_args, filename), Attr: attr, EndFunction: newAstNode(n.endfunction, filename).(*ast.EndFunction), } @@ -100,13 +101,13 @@ func newAstNode(n *VimNode, filename string) ast.Node { FuncCall: newAstNode(n.left, filename).(*ast.CallExpr), } - case NODE_LET: + case NODE_LET, NODE_CONST: return &ast.Let{ Let: pos, ExArg: newExArg(*n.ea, filename), Op: n.op, Left: newExprNode(n.left, filename), - List: newList(*n, filename), + List: newExprs(n.list, filename), Rest: newExprNode(n.rest, filename), Right: newExprNode(n.right, filename), } @@ -115,7 +116,7 @@ func newAstNode(n *VimNode, filename string) ast.Node { return &ast.UnLet{ UnLet: pos, ExArg: newExArg(*n.ea, filename), - List: newList(*n, filename), + List: newExprs(n.list, filename), } case NODE_LOCKVAR: @@ -123,7 +124,7 @@ func newAstNode(n *VimNode, filename string) ast.Node { LockVar: pos, ExArg: newExArg(*n.ea, filename), Depth: n.depth, - List: newList(*n, filename), + List: newExprs(n.list, filename), } case NODE_UNLOCKVAR: @@ -131,7 +132,7 @@ func newAstNode(n *VimNode, filename string) ast.Node { UnLockVar: pos, ExArg: newExArg(*n.ea, filename), Depth: n.depth, - List: newList(*n, filename), + List: newExprs(n.list, filename), } case NODE_IF: @@ -200,7 +201,7 @@ func newAstNode(n *VimNode, filename string) ast.Node { ExArg: newExArg(*n.ea, filename), Body: newBody(*n, filename), Left: newExprNode(n.left, filename), - List: newList(*n, filename), + List: newExprs(n.list, filename), Rest: newExprNode(n.rest, filename), Right: newExprNode(n.right, filename), EndFor: newAstNode(n.endfor, filename).(*ast.EndFor), @@ -275,12 +276,19 @@ func newAstNode(n *VimNode, filename string) ast.Node { Expr: newExprNode(n.left, filename), } + case NODE_EVAL: + return &ast.Eval{ + Eval: pos, + ExArg: newExArg(*n.ea, filename), + Expr: newExprNode(n.left, filename), + } + case NODE_ECHO, NODE_ECHON, NODE_ECHOMSG, NODE_ECHOERR: return &ast.EchoCmd{ Start: pos, CmdName: n.ea.cmd.name, ExArg: newExArg(*n.ea, filename), - Exprs: newList(*n, filename), + Exprs: newExprs(n.list, filename), } case NODE_ECHOHL: @@ -294,7 +302,7 @@ func newAstNode(n *VimNode, filename string) ast.Node { return &ast.Execute{ Execute: pos, ExArg: newExArg(*n.ea, filename), - Exprs: newList(*n, filename), + Exprs: newExprs(n.list, filename), } case NODE_TERNARY: @@ -343,11 +351,19 @@ func newAstNode(n *VimNode, filename string) ast.Node { High: newExprNode(n.rlist[1], filename), } + case NODE_METHOD: + return &ast.MethodExpr{ + Lparen: pos, + Left: newExprNode(n.left, filename), + Method: newExprNode(n.right.left, filename), + Args: newExprs(n.right.rlist, filename), + } + case NODE_CALL: return &ast.CallExpr{ Lparen: pos, Fun: newExprNode(n.left, filename), - Args: newRlist(*n, filename), + Args: newExprs(n.rlist, filename), } case NODE_DOT: @@ -446,6 +462,21 @@ func newAstNode(n *VimNode, filename string) ast.Node { Expr: newExprNode(n.left, filename), } + case NODE_BLOB: + return &ast.BasicLit{ + ValuePos: pos, + Kind: token.BLOB, + Value: n.value.(string), + } + + case NODE_HEREDOC: + return &ast.HeredocExpr{ + OpPos: pos, + Flags: newExprs(n.rlist, filename), + EndMarker: n.op, + Body: newExprs(n.body, filename), + } + case NODE_PARENEXPR: n := n.value.(*VimNode) return &ast.ParenExpr{ @@ -541,25 +572,12 @@ func newIdents(n VimNode, filename string) []*ast.Ident { return idents } -func newRlist(n VimNode, filename string) []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, newExprNode(node, filename)) - } - } - return exprs -} - -func newList(n VimNode, filename string) []ast.Expr { +func newExprs(xs []*VimNode, filename string) []ast.Expr { var list []ast.Expr - if n.list != nil { - list = make([]ast.Expr, 0, len(n.list)) + if xs != nil { + list = make([]ast.Expr, 0, len(xs)) } - for _, node := range n.list { + for _, node := range xs { if node != nil { // conservative list = append(list, newExprNode(node, filename)) } diff --git a/go/gocompiler.vim b/go/gocompiler.vim index 28f088a..fbc0cbb 100644 --- a/go/gocompiler.vim +++ b/go/gocompiler.vim @@ -452,24 +452,13 @@ function s:GoCompiler.compile_let(node) elseif left =~ '^\v(self\.(find_command_cache|cache|buf|pos|context)|toplevel.body|lhs.list|(node\.(body|attr|else_|elseif|catch|finally|pattern|end(function|if|for|try))))$' && op == '=' " skip initialization return - elseif left =~ '^\v(node\.(list|depth))$' && op == '=' - if right == 'nil' || right == '[]interface{}{}' - return - endif - call self.out('%s %s %s', left, op, right) + elseif left =~ '^\v%(node\.%(r?list|default_args|body))$' && op == '=' + call self.out('%s %s %s', left, op, substitute(right, '[]interface{}', '[]*VimNode', 'g')) return - elseif left =~ 'node.rlist' && op == '=' - if right == '[]interface{}{}' - return - endif - let m = matchstr(right, '\V[]interface{}{\zs\.\*\ze}\$') - if m != '' - call self.out('%s = []*VimNode{%s}', left, m) - else - call self.out('%s = %s', left, right) - endif + elseif left == 'node.depth' && op == '=' && right == 'nil' + call self.out('%s %s %s', left, op, '0') return - elseif left =~ '^\v(list|curly_parts)$' && op == '=' && right == '[]interface{}{}' + elseif left =~ '^\v(r?list|curly_parts)$' && op == '=' && right == '[]interface{}{}' call self.out('var %s []*VimNode', left) return elseif left == 'cmd' && op == '=' && (right == 'nil' || right =~ '^\Vmap[string]interface{}{') @@ -526,7 +515,7 @@ function s:GoCompiler.compile_let(node) endfunction function s:GoCompiler.compile_unlet(node) - echom 'NotImplemented: unlet' + " echom 'NotImplemented: unlet' endfunction function s:GoCompiler.compile_lockvar(node) @@ -572,7 +561,9 @@ function s:GoCompiler.compile_while(node) endif call self.out('for %s{', cond) call self.incindent("\t") + call self.inscope() call self.compile_body(a:node.body) + call self.descope() call self.decindent() call self.out('}') endfunction @@ -651,6 +642,10 @@ function s:GoCompiler.compile_ternary(node) let right = self.compile(a:node.right) if cond =~ '^node\.rlist\[\d\]' && left == '"nil"' return printf('func() string { if %s {return %s} else {return %s.(string)} }()', cond, left, right) + elseif cond =~ '^viml_empty' && left == '"(list)"' + return printf('func() string { if %s {return %s} else {return %s} }()', cond, left, right) + elseif cond == 'is_litdict' + return printf('func() *VimNode { if %s {return %s} else {return %s} }()', cond, left, right) else return printf('viml_ternary(%s, %s, %s)', cond, left, right) endif @@ -769,7 +764,7 @@ function s:GoCompiler.compile_isci(node) endfunction function s:GoCompiler.compile_iscs(node) - throw 'NotImplemented: is#' + return self.compile_op2(a:node, '==') endfunction function s:GoCompiler.compile_isnot(node) @@ -781,7 +776,7 @@ function s:GoCompiler.compile_isnotci(node) endfunction function s:GoCompiler.compile_isnotcs(node) - throw 'NotImplemented: isnot#' + return self.compile_op2(a:node, '!=') endfunction function s:GoCompiler.compile_add(node) @@ -848,7 +843,6 @@ function s:GoCompiler.compile_call(node) let rlist = map(a:node.rlist, 'self.compile(v:val)') let left = self.compile(a:node.left) if left == 'map' && len(rlist) == 2 && rlist[1] == '"self.compile(v:val)"' - " throw 'NotImplemented: map()' return printf(join([ \ 'func() []string {', \ 'var ss []string', @@ -858,12 +852,26 @@ function s:GoCompiler.compile_call(node) \ 'return ss', \ '}()', \ ], ";"), rlist[0], substitute(rlist[1][1:-2], 'v:val', 'vval', 'g')) + elseif left == 'map' && len(rlist) == 2 && rlist[1] == '"self.escape_string(v:val.value)"' + return printf(join([ + \ 'func() []string {', + \ 'var ss []string', + \ 'for _, vval := range %s {', + \ 'ss = append(ss, %s)', + \ '}', + \ 'return ss', + \ '}()', + \ ], ";"), rlist[0], substitute(rlist[1][1:-2], 'v:val\.value', 'vval.value.(string)', 'g')) elseif left == 'call' && rlist[0][0] =~ '[''"]' return printf('viml_%s(*%s)', rlist[0][1:-2], rlist[1]) elseif left =~ 'ExArg' return printf('&%s{}', left) + elseif left == 'remove' && len(rlist) == 2 && rlist[1] == '-1' + return printf('%s = %s[:len(%s)-1]', rlist[0], rlist[0], rlist[0]) elseif left == 'isvarname' && len(rlist) == 1 && rlist[0] == 'node.value' return printf('%s(%s.(string))', left, rlist[0]) + elseif left == 'islower' && len(rlist) == 1 && rlist[0] == 'key[0]' + return printf('%s(key[:1])', left) elseif left == 'self.reader.seek_set' && len(rlist) == 1 && rlist[0] == 'x[0]' return printf('%s(%s.(int))', left, rlist[0]) elseif left == 'self.compile' && len(rlist) == 1 && rlist[0] =~ '\v^node\.(left|rest)$' @@ -882,6 +890,9 @@ function s:GoCompiler.compile_call(node) endif endif if left == 'range_' + if len(rlist) == 1 + let rlist = ['0', rlist[0] . ' - 1'] + endif let left = 'viml_range' endif return printf('%s(%s)', left, join(rlist, ', ')) @@ -991,7 +1002,7 @@ function s:GoCompiler.compile_op2(node, op) return printf('%s %s %s', left, a:op, right) endfunction -let s:viml_builtin_functions = ['abs', 'acos', 'add', 'and', 'append', 'append', 'argc', 'argidx', 'argv', 'argv', 'asin', 'atan', 'atan2', 'browse', 'browsedir', 'bufexists', 'buflisted', 'bufloaded', 'bufname', 'bufnr', 'bufwinnr', 'byte2line', 'byteidx', 'call', 'ceil', 'changenr', 'char2nr', 'cindent', 'clearmatches', 'col', 'complete', 'complete_add', 'complete_check', 'confirm', 'copy', 'cos', 'cosh', 'count', 'cscope_connection', 'cursor', 'cursor', 'deepcopy', 'delete', 'did_filetype', 'diff_filler', 'diff_hlID', 'empty', 'escape', 'eval', 'eventhandler', 'executable', 'exists', 'extend', 'exp', 'expand', 'feedkeys', 'filereadable', 'filewritable', 'filter', 'finddir', 'findfile', 'float2nr', 'floor', 'fmod', 'fnameescape', 'fnamemodify', 'foldclosed', 'foldclosedend', 'foldlevel', 'foldtext', 'foldtextresult', 'foreground', 'function', 'garbagecollect', 'get', 'get', 'getbufline', 'getbufvar', 'getchar', 'getcharmod', 'getcmdline', 'getcmdpos', 'getcmdtype', 'getcwd', 'getfperm', 'getfsize', 'getfontname', 'getftime', 'getftype', 'getline', 'getline', 'getloclist', 'getmatches', 'getpid', 'getpos', 'getqflist', 'getreg', 'getregtype', 'gettabvar', 'gettabwinvar', 'getwinposx', 'getwinposy', 'getwinvar', 'glob', 'globpath', 'has', 'has_key', 'haslocaldir', 'hasmapto', 'histadd', 'histdel', 'histget', 'histnr', 'hlexists', 'hlID', 'hostname', 'iconv', 'indent', 'index', 'input', 'inputdialog', 'inputlist', 'inputrestore', 'inputsave', 'inputsecret', 'insert', 'invert', 'isdirectory', 'islocked', 'items', 'join', 'keys', 'len', 'libcall', 'libcallnr', 'line', 'line2byte', 'lispindent', 'localtime', 'log', 'log10', 'luaeval', 'map', 'maparg', 'mapcheck', 'match', 'matchadd', 'matcharg', 'matchdelete', 'matchend', 'matchlist', 'matchstr', 'max', 'min', 'mkdir', 'mode', 'mzeval', 'nextnonblank', 'nr2char', 'or', 'pathshorten', 'pow', 'prevnonblank', 'printf', 'pumvisible', 'pyeval', 'py3eval', 'range', 'readfile', 'reltime', 'reltimestr', 'remote_expr', 'remote_foreground', 'remote_peek', 'remote_read', 'remote_send', 'remove', 'remove', 'rename', 'repeat', 'resolve', 'reverse', 'round', 'screencol', 'screenrow', 'search', 'searchdecl', 'searchpair', 'searchpairpos', 'searchpos', 'server2client', 'serverlist', 'setbufvar', 'setcmdpos', 'setline', 'setloclist', 'setmatches', 'setpos', 'setqflist', 'setreg', 'settabvar', 'settabwinvar', 'setwinvar', 'sha256', 'shellescape', 'shiftwidth', 'simplify', 'sin', 'sinh', 'sort', 'soundfold', 'spellbadword', 'spellsuggest', 'split', 'sqrt', 'str2float', 'str2nr', 'strchars', 'strdisplaywidth', 'strftime', 'stridx', 'string', 'strlen', 'strpart', 'strridx', 'strtrans', 'strwidth', 'submatch', 'substitute', 'synID', 'synIDattr', 'synIDtrans', 'synconcealed', 'synstack', 'system', 'tabpagebuflist', 'tabpagenr', 'tabpagewinnr', 'taglist', 'tagfiles', 'tempname', 'tan', 'tanh', 'tolower', 'toupper', 'tr', 'trunc', 'type', 'undofile', 'undotree', 'values', 'virtcol', 'visualmode', 'wildmenumode', 'winbufnr', 'wincol', 'winheight', 'winline', 'winnr', 'winrestcmd', 'winrestview', 'winsaveview', 'winwidth', 'writefile', 'xor'] +let s:viml_builtin_functions = map(copy(s:VimLParser.builtin_functions), 'v:val.name') function! s:test() let vimfile = 'autoload/vimlparser.vim' diff --git a/go/type.go b/go/type.go index eb19360..e34d543 100644 --- a/go/type.go +++ b/go/type.go @@ -40,19 +40,20 @@ type Cmd struct { } type VimNode struct { - type_ int // type -> type_ - pos *pos - left *VimNode - right *VimNode - cond *VimNode - rest *VimNode - list []*VimNode - rlist []*VimNode - body []*VimNode - op string - str string - depth int - value interface{} + type_ int // type -> type_ + pos *pos + left *VimNode + right *VimNode + cond *VimNode + rest *VimNode + list []*VimNode + rlist []*VimNode + default_args []*VimNode + body []*VimNode + op string + str string + depth int + value interface{} ea *ExArg attr *FuncAttr diff --git a/go/typedefs.vim b/go/typedefs.vim index 149690a..c53be39 100644 --- a/go/typedefs.vim +++ b/go/typedefs.vim @@ -61,6 +61,10 @@ call extend(s:typedefs.func, { \ 'in': [], \ 'out': ['*pos'], \ }, +\ 'VimLParser.parse_heredoc': { +\ 'in': [], +\ 'out': ['*VimNode'], +\ }, \ 'VimLParser.parse_expr': { \ 'in': [], \ 'out': ['*VimNode'], @@ -77,6 +81,10 @@ call extend(s:typedefs.func, { \ 'in': [], \ 'out': ['*VimNode'], \ }, +\ 'VimLParser.parse_constlvalue': { +\ 'in': [], +\ 'out': ['*VimNode'], +\ }, \ 'VimLParser.parse_lvaluelist': { \ 'in': [], \ 'out': ['[]*VimNode'], @@ -85,6 +93,10 @@ call extend(s:typedefs.func, { \ 'in': [], \ 'out': ['*lhs'], \ }, +\ 'VimLParser.parse_constlhs': { +\ 'in': [], +\ 'out': ['*lhs'], +\ }, \ 'VimLParser.ends_excmds': { \ 'in': ['string'], \ 'out': ['bool'], @@ -110,6 +122,7 @@ call extend(s:typedefs.func, { \ 'ExprTokenizer.get2': { 'in': [], 'out': ['*ExprToken'] }, \ 'ExprTokenizer.get_sstring': { 'in': [], 'out': ['string'] }, \ 'ExprTokenizer.get_dstring': { 'in': [], 'out': ['string'] }, +\ 'ExprTokenizer.parse_dict_literal_key': { 'in': [], 'out': ['*VimNode'] }, \ }) call extend(s:typedefs.func, { @@ -127,7 +140,9 @@ call extend(s:typedefs.func, { \ 'ExprParser.parse_expr7': { 'in': [], 'out': ['*VimNode'] }, \ 'ExprParser.parse_expr8': { 'in': [], 'out': ['*VimNode'] }, \ 'ExprParser.parse_expr9': { 'in': [], 'out': ['*VimNode'] }, +\ 'ExprParser.parse_rlist': { 'in': [], 'out': ['[]*VimNode'] }, \ 'ExprParser.parse_dot': { 'in': ['*ExprToken', '*VimNode'], 'out': ['*VimNode'] }, +\ 'ExprParser.parse_concat': { 'in': ['*ExprToken', '*VimNode'], 'out': ['*VimNode'] }, \ 'ExprParser.parse_identifier': { 'in': [], 'out': ['*VimNode'] }, \ 'ExprParser.parse_curly_parts': { 'in': [], 'out': ['[]*VimNode'] }, \ }) @@ -207,7 +222,9 @@ call extend(s:typedefs.func, { \ 'StringReader.read_alnum': { 'in': [], 'out': ['string'] }, \ 'StringReader.read_digit': { 'in': [], 'out': ['string'] }, \ 'StringReader.read_odigit': { 'in': [], 'out': ['string'] }, +\ 'StringReader.read_blob': { 'in': [], 'out': ['string'] }, \ 'StringReader.read_xdigit': { 'in': [], 'out': ['string'] }, +\ 'StringReader.read_bdigit': { 'in': [], 'out': ['string'] }, \ 'StringReader.read_integer': { 'in': [], 'out': ['string'] }, \ 'StringReader.read_word': { 'in': [], 'out': ['string'] }, \ 'StringReader.read_white': { 'in': [], 'out': ['string'] }, @@ -242,7 +259,9 @@ call extend(s:typedefs.func, { \ 'Compiler.compile_delfunction': { 'in': ['*VimNode'], 'out': [] }, \ 'Compiler.compile_return': { 'in': ['*VimNode'], 'out': [] }, \ 'Compiler.compile_excall': { 'in': ['*VimNode'], 'out': [] }, +\ 'Compiler.compile_eval': { 'in': ['*VimNode'], 'out': [] }, \ 'Compiler.compile_let': { 'in': ['*VimNode'], 'out': [] }, +\ 'Compiler.compile_const': { 'in': ['*VimNode'], 'out': [] }, \ 'Compiler.compile_unlet': { 'in': ['*VimNode'], 'out': [] }, \ 'Compiler.compile_lockvar': { 'in': ['*VimNode'], 'out': [] }, \ 'Compiler.compile_unlockvar': { 'in': ['*VimNode'], 'out': [] }, @@ -305,8 +324,10 @@ call extend(s:typedefs.func, { \ 'Compiler.compile_subscript': { 'in': ['*VimNode'], 'out': ['string'] }, \ 'Compiler.compile_slice': { 'in': ['*VimNode'], 'out': ['string'] }, \ 'Compiler.compile_dot': { 'in': ['*VimNode'], 'out': ['string'] }, +\ 'Compiler.compile_method': { 'in': ['*VimNode'], 'out': ['string'] }, \ 'Compiler.compile_call': { 'in': ['*VimNode'], 'out': ['string'] }, \ 'Compiler.compile_number': { 'in': ['*VimNode'], 'out': ['string'] }, +\ 'Compiler.compile_blob': { 'in': ['*VimNode'], 'out': ['string'] }, \ 'Compiler.compile_string': { 'in': ['*VimNode'], 'out': ['string'] }, \ 'Compiler.compile_list': { 'in': ['*VimNode'], 'out': ['string'] }, \ 'Compiler.compile_dict': { 'in': ['*VimNode'], 'out': ['string'] }, @@ -317,7 +338,9 @@ call extend(s:typedefs.func, { \ 'Compiler.compile_reg': { 'in': ['*VimNode'], 'out': ['string'] }, \ 'Compiler.compile_curlynamepart': { 'in': ['*VimNode'], 'out': ['string'] }, \ 'Compiler.compile_curlynameexpr': { 'in': ['*VimNode'], 'out': ['string'] }, +\ 'Compiler.escape_string': { 'in': ['string'], 'out': ['string'] }, \ 'Compiler.compile_lambda': { 'in': ['*VimNode'], 'out': ['string'] }, +\ 'Compiler.compile_heredoc': { 'in': ['*VimNode'], 'out': ['string'] }, \ 'Compiler.compile_parenexpr': { 'in': ['*VimNode'], 'out': ['string'] }, \ }) diff --git a/go/vimlfunc.go b/go/vimlfunc.go index 26cd194..8896232 100644 --- a/go/vimlfunc.go +++ b/go/vimlfunc.go @@ -20,31 +20,31 @@ var patVim2Go = map[string]string{ "\\": "\\bUSECTRLV\\b", "\\": "\\bUSERCMD\\b", "\\<\\(XFILE\\|FILES\\|FILE1\\)\\>": "\\b(XFILE|FILES|FILE1)\\b", - "\\S": "\\S", - "\\a": "[A-Za-z]", - "\\d": "\\d", - "\\h": "[A-Za-z_]", - "\\s": "\\s", - "\\v^d%[elete][lp]$": "^d(elete|elet|ele|el|e)[lp]$", + "\\S": "\\S", + "\\a": "[A-Za-z]", + "\\d": "\\d", + "\\h": "[A-Za-z_]", + "\\s": "\\s", + "\\v^d%[elete][lp]$": "^d(elete|elet|ele|el|e)[lp]$", "\\v^s%(c[^sr][^i][^p]|g|i[^mlg]|I|r[^e])": "^s(c[^sr][^i][^p]|g|i[^mlg]|I|r[^e])", - "\\w": "[0-9A-Za-z_]", - "\\w\\|[:#]": "[0-9A-Za-z_]|[:#]", - "\\x": "[0-9A-Fa-f]", - "^++": "^\\+\\+", - "^++bad=\\(keep\\|drop\\|.\\)\\>": "^\\+\\+bad=(keep|drop|.)\\b", - "^++bad=drop": "^\\+\\+bad=drop", - "^++bad=keep": "^\\+\\+bad=keep", - "^++bin\\>": "^\\+\\+bin\\b", - "^++edit\\>": "^\\+\\+edit\\b", - "^++enc=\\S": "^\\+\\+enc=\\S", - "^++encoding=\\S": "^\\+\\+encoding=\\S", - "^++ff=\\(dos\\|unix\\|mac\\)\\>": "^\\+\\+ff=(dos|unix|mac)\\b", - "^++fileformat=\\(dos\\|unix\\|mac\\)\\>": "^\\+\\+fileformat=(dos|unix|mac)\\b", - "^++nobin\\>": "^\\+\\+nobin\\b", - "^[A-Z]": "^[A-Z]", - "^\\$\\w\\+": "^\\$[0-9A-Za-z_]+", - "^\\(!\\|global\\|vglobal\\)$": "^(!|global|vglobal)$", - "^\\(WHILE\\|FOR\\)$": "^(WHILE|FOR)$", + "\\w": "[0-9A-Za-z_]", + "\\w\\|[:#]": "[0-9A-Za-z_]|[:#]", + "\\x": "[0-9A-Fa-f]", + "^++": "^\\+\\+", + "^++bad=\\(keep\\|drop\\|.\\)\\>": "^\\+\\+bad=(keep|drop|.)\\b", + "^++bad=drop": "^\\+\\+bad=drop", + "^++bad=keep": "^\\+\\+bad=keep", + "^++bin\\>": "^\\+\\+bin\\b", + "^++edit\\>": "^\\+\\+edit\\b", + "^++enc=\\S": "^\\+\\+enc=\\S", + "^++encoding=\\S": "^\\+\\+encoding=\\S", + "^++ff=\\(dos\\|unix\\|mac\\)\\>": "^\\+\\+ff=(dos|unix|mac)\\b", + "^++fileformat=\\(dos\\|unix\\|mac\\)\\>": "^\\+\\+fileformat=(dos|unix|mac)\\b", + "^++nobin\\>": "^\\+\\+nobin\\b", + "^[A-Z]": "^[A-Z]", + "^\\$\\w\\+": "^\\$[0-9A-Za-z_]+", + "^\\(!\\|global\\|vglobal\\)$": "^(!|global|vglobal)$", + "^\\(WHILE\\|FOR\\)$": "^(WHILE|FOR)$", "^\\(vimgrep\\|vimgrepadd\\|lvimgrep\\|lvimgrepadd\\)$": "^(vimgrep|vimgrepadd|lvimgrep|lvimgrepadd)$", "^\\d": "^\\d", "^\\h": "^[A-Za-z_]", @@ -62,7 +62,11 @@ var patVim2Go = map[string]string{ "^[A-Z]$": "^[A-Z]$", "^[a-z]$": "^[a-z]$", "^[vgslabwt]:$\\|^\\([vgslabwt]:\\)\\?[A-Za-z_][0-9A-Za-z_#]*$": "^[vgslabwt]:$|^([vgslabwt]:)?[A-Za-z_][0-9A-Za-z_#]*$", - "^[0-7]$": "^[0-7]$", + "^[0-7]$": "^[0-7]$", + "^[0-9A-Fa-f][0-9A-Fa-f]$": "^[0-9A-Fa-f][0-9A-Fa-f]$", + "^\\.[0-9A-Fa-f]$": "^\\.[0-9A-Fa-f]$", + "^[0-9A-Fa-f][^0-9A-Fa-f]$": "^[0-9A-Fa-f][^0-9A-Fa-f]$", + "^[^a-z]\\S\\+$": "^[^a-z]\\S+$", } var patVim2GoRegh = make(map[string]*regexp.Regexp) diff --git a/go/vimlparser.go b/go/vimlparser.go index d52d5fd..d642d7a 100644 --- a/go/vimlparser.go +++ b/go/vimlparser.go @@ -95,7 +95,12 @@ var NODE_REG = 89 var NODE_CURLYNAMEPART = 90 var NODE_CURLYNAMEEXPR = 91 var NODE_LAMBDA = 92 -var NODE_PARENEXPR = 93 +var NODE_BLOB = 93 +var NODE_CONST = 94 +var NODE_EVAL = 95 +var NODE_HEREDOC = 96 +var NODE_METHOD = 97 +var NODE_PARENEXPR = 200 var TOKEN_EOF = 1 var TOKEN_EOL = 2 var TOKEN_SPACE = 3 @@ -161,6 +166,10 @@ var TOKEN_BACKTICK = 62 var TOKEN_DOTDOTDOT = 63 var TOKEN_SHARP = 64 var TOKEN_ARROW = 65 +var TOKEN_BLOB = 66 +var TOKEN_LITCOPEN = 67 +var TOKEN_DOTDOT = 68 +var TOKEN_HEREDOC = 69 var MAX_FUNC_ARGS = 20 func isalpha(c string) bool { @@ -233,6 +242,7 @@ func islower(c string) bool { // node rest // node[] list // node[] rlist +// node[] default_args // node[] body // string op // string str @@ -242,12 +252,13 @@ func islower(c string) bool { // TOPLEVEL .body // COMMENT .str // EXCMD .ea .str -// FUNCTION .ea .body .left .rlist .attr .endfunction +// FUNCTION .ea .body .left .rlist .default_args .attr .endfunction // ENDFUNCTION .ea // DELFUNCTION .ea .left // RETURN .ea .left // EXCALL .ea .left // LET .ea .op .left .list .rest .right +// CONST .ea .op .left .list .rest .right // UNLET .ea .list // LOCKVAR .ea .depth .list // UNLOCKVAR .ea .depth .list @@ -266,6 +277,7 @@ func islower(c string) bool { // FINALLY .ea .body // ENDTRY .ea // THROW .ea .left +// EVAL .ea .left // ECHO .ea .list // ECHON .ea .list // ECHOHL .ea .str @@ -316,12 +328,14 @@ func islower(c string) bool { // PLUS .left // SUBSCRIPT .left .right // SLICE .left .rlist +// METHOD .left .right // CALL .left .rlist // DOT .left .right // NUMBER .value // STRING .value // LIST .value // DICT .value +// BLOB .value // NESTING .left // OPTION .value // IDENTIFIER .value @@ -331,6 +345,7 @@ func islower(c string) bool { // CURLYNAMEPART .value // CURLYNAMEEXPR .value // LAMBDA .rlist .left +// HEREDOC .rlist .op .body // PARENEXPR .value func (self *VimLParser) find_context(type_ int) int { var i = 0 @@ -380,6 +395,7 @@ func (self *VimLParser) check_missing_endfor(ends string, pos *pos) { func (self *VimLParser) parse(reader *StringReader) *VimNode { self.reader = reader var toplevel = Node(NODE_TOPLEVEL) + toplevel.pos = self.reader.getpos() self.push_context(toplevel) for self.reader.peek() != "" { self.parse_one_cmd() @@ -458,7 +474,7 @@ func (self *VimLParser) parse_command_modifiers() { // keepp\%[atterns] modifiers = append(modifiers, map[string]interface{}{"name": "keeppatterns"}) } else if viml_stridx("hide", k) == 0 && len(k) >= 3 { - //hid\%[e] + // hid\%[e] if self.ends_excmds(c) { break } @@ -476,7 +492,7 @@ func (self *VimLParser) parse_command_modifiers() { // :nos\%[wapfile] modifiers = append(modifiers, map[string]interface{}{"name": "noswapfile"}) } else if viml_stridx("rightbelow", k) == 0 && len(k) >= 6 { - //rightb\%[elow] + // rightb\%[elow] modifiers = append(modifiers, map[string]interface{}{"name": "rightbelow"}) } else if viml_stridx("sandbox", k) == 0 && len(k) >= 3 { // san\%[dbox] @@ -690,6 +706,7 @@ func (self *VimLParser) parse_command() { self._parse_command(self.ea.cmd.parser) } +// TODO: self[a:parser] func (self *VimLParser) _parse_command(parser string) { if parser == "parse_cmd_append" { self.parse_cmd_append() @@ -745,6 +762,8 @@ func (self *VimLParser) _parse_command(parser string) { self.parse_cmd_insert() } else if parser == "parse_cmd_let" { self.parse_cmd_let() + } else if parser == "parse_cmd_const" { + self.parse_cmd_const() } else if parser == "parse_cmd_loadkeymap" { self.parse_cmd_loadkeymap() } else if parser == "parse_cmd_lockvar" { @@ -769,6 +788,8 @@ func (self *VimLParser) _parse_command(parser string) { self.parse_cmd_tcl() } else if parser == "parse_cmd_throw" { self.parse_cmd_throw() + } else if parser == "parse_cmd_eval" { + self.parse_cmd_eval() } else if parser == "parse_cmd_try" { self.parse_cmd_try() } else if parser == "parse_cmd_unlet" { @@ -1199,7 +1220,7 @@ func (self *VimLParser) parse_cmd_function() { if left.type_ == NODE_IDENTIFIER { var s = left.value.(string) var ss = viml_split(s, "\\zs") - if ss[0] != "<" && !isupper(ss[0]) && viml_stridx(s, ":") == -1 && viml_stridx(s, "#") == -1 { + if ss[0] != "<" && ss[0] != "_" && !isupper(ss[0]) && viml_stridx(s, ":") == -1 && viml_stridx(s, "#") == -1 { panic(Err(viml_printf("E128: Function name must start with a capital or contain a colon: %s", s), left.pos)) } } @@ -1214,6 +1235,8 @@ func (self *VimLParser) parse_cmd_function() { node.pos = self.ea.cmdpos node.ea = self.ea node.left = left + node.rlist = []*VimNode{} + node.default_args = []*VimNode{} self.reader.getn(1) var tokenizer = NewExprTokenizer(self.reader) if tokenizer.peek().type_ == TOKEN_PCLOSE { @@ -1233,6 +1256,12 @@ func (self *VimLParser) parse_cmd_function() { varnode.pos = token.pos varnode.value = token.value node.rlist = append(node.rlist, varnode) + if tokenizer.peek().type_ == TOKEN_EQ { + tokenizer.get() + node.default_args = append(node.default_args, self.parse_expr()) + } else if len(node.default_args) > 0 { + panic(Err("E989: Non-default argument follows default argument", varnode.pos)) + } // XXX: Vim doesn't skip white space before comma. F(a ,b) => E475 if iswhite(self.reader.p(0)) && tokenizer.peek().type_ == TOKEN_COMMA { panic(Err("E475: Invalid argument: White space is not allowed before comma", self.reader.getpos())) @@ -1342,6 +1371,50 @@ func (self *VimLParser) parse_cmd_call() { self.add_node(node) } +func (self *VimLParser) parse_heredoc() *VimNode { + var node = Node(NODE_HEREDOC) + node.pos = self.ea.cmdpos + node.op = "" + node.rlist = []*VimNode{} + for true { + self.reader.skip_white() + var pos = self.reader.getpos() + var key = self.reader.read_word() + if key == "" { + break + } + if !islower(key[:1]) { + node.op = key + break + } else { + var keynode = Node(NODE_STRING) + keynode.pos = pos + keynode.value = key + node.rlist = append(node.rlist, keynode) + } + } + if node.op == "" { + panic(Err("E172: Missing marker", self.reader.getpos())) + } + self.parse_trail() + for true { + if self.reader.peek() == "" { + break + } + var pos = self.reader.getpos() + var line = self.reader.getn(-1) + if line == node.op { + return node + } + var linenode = Node(NODE_STRING) + linenode.pos = pos + linenode.value = line + node.body = append(node.body, linenode) + self.reader.get() + } + panic(Err(viml_printf("E990: Missing end marker '%s'", node.op), self.reader.getpos())) +} + func (self *VimLParser) parse_cmd_let() { var pos = self.reader.tell() self.reader.skip_white() @@ -1355,8 +1428,14 @@ func (self *VimLParser) parse_cmd_let() { self.reader.skip_white() var s1 = self.reader.peekn(1) var s2 = self.reader.peekn(2) + // TODO check scriptversion? + if s2 == ".." { + s2 = self.reader.peekn(3) + } else if s2 == "=<" { + s2 = self.reader.peekn(3) + } // :let {var-name} .. - if self.ends_excmds(s1) || (s2 != "+=" && s2 != "-=" && s2 != ".=" && s1 != "=") { + if self.ends_excmds(s1) || (s2 != "+=" && s2 != "-=" && s2 != ".=" && s2 != "..=" && s2 != "*=" && s2 != "/=" && s2 != "%=" && s2 != "=<<" && s1 != "=") { self.reader.seek_set(pos) self.parse_cmd_common() return @@ -1370,9 +1449,16 @@ func (self *VimLParser) parse_cmd_let() { node.list = lhs.list node.rest = lhs.rest node.right = nil - if s2 == "+=" || s2 == "-=" || s2 == ".=" { - self.reader.getn(2) + if s2 == "+=" || s2 == "-=" || s2 == ".=" || s2 == "..=" || s2 == "*=" || s2 == "/=" || s2 == "%=" { + self.reader.getn(len(s2)) + node.op = s2 + } else if s2 == "=<<" { + self.reader.getn(len(s2)) + self.reader.skip_white() node.op = s2 + node.right = self.parse_heredoc() + self.add_node(node) + return } else if s1 == "=" { self.reader.getn(1) node.op = s1 @@ -1383,6 +1469,37 @@ func (self *VimLParser) parse_cmd_let() { self.add_node(node) } +func (self *VimLParser) parse_cmd_const() { + var pos = self.reader.tell() + self.reader.skip_white() + // :const + if self.ends_excmds(self.reader.peek()) { + self.reader.seek_set(pos) + self.parse_cmd_common() + return + } + var lhs = self.parse_constlhs() + self.reader.skip_white() + var s1 = self.reader.peekn(1) + // :const {var-name} + if self.ends_excmds(s1) || s1 != "=" { + self.reader.seek_set(pos) + self.parse_cmd_common() + return + } + // :const left op right + var node = Node(NODE_CONST) + node.pos = self.ea.cmdpos + node.ea = self.ea + self.reader.getn(1) + node.op = s1 + node.left = lhs.left + node.list = lhs.list + node.rest = lhs.rest + node.right = self.parse_expr() + self.add_node(node) +} + func (self *VimLParser) parse_cmd_unlet() { var node = Node(NODE_UNLET) node.pos = self.ea.cmdpos @@ -1395,6 +1512,8 @@ func (self *VimLParser) parse_cmd_lockvar() { var node = Node(NODE_LOCKVAR) node.pos = self.ea.cmdpos node.ea = self.ea + node.depth = 0 + node.list = []*VimNode{} self.reader.skip_white() if isdigit(self.reader.peekn(1)) { node.depth = viml_str2nr(self.reader.read_digit(), 10) @@ -1407,6 +1526,8 @@ func (self *VimLParser) parse_cmd_unlockvar() { var node = Node(NODE_UNLOCKVAR) node.pos = self.ea.cmdpos node.ea = self.ea + node.depth = 0 + node.list = []*VimNode{} self.reader.skip_white() if isdigit(self.reader.peekn(1)) { node.depth = viml_str2nr(self.reader.read_digit(), 10) @@ -1603,6 +1724,14 @@ func (self *VimLParser) parse_cmd_throw() { self.add_node(node) } +func (self *VimLParser) parse_cmd_eval() { + var node = Node(NODE_EVAL) + node.pos = self.ea.cmdpos + node.ea = self.ea + node.left = self.parse_expr() + self.add_node(node) +} + func (self *VimLParser) parse_cmd_echo() { var node = Node(NODE_ECHO) node.pos = self.ea.cmdpos @@ -1696,6 +1825,29 @@ func (self *VimLParser) parse_lvalue() *VimNode { panic(Err("Invalid Expression", node.pos)) } +// TODO: merge with s:VimLParser.parse_lvalue() +func (self *VimLParser) parse_constlvalue() *VimNode { + var p = NewLvalueParser(self.reader) + var node = p.parse() + if node.type_ == NODE_IDENTIFIER { + if !isvarname(node.value.(string)) { + panic(Err(viml_printf("E461: Illegal variable name: %s", node.value), node.pos)) + } + } + if node.type_ == NODE_IDENTIFIER || node.type_ == NODE_CURLYNAME { + return node + } else if node.type_ == NODE_SUBSCRIPT || node.type_ == NODE_SLICE || node.type_ == NODE_DOT { + panic(Err("E996: Cannot lock a list or dict", node.pos)) + } else if node.type_ == NODE_OPTION { + panic(Err("E996: Cannot lock an option", node.pos)) + } else if node.type_ == NODE_ENV { + panic(Err("E996: Cannot lock an environment variable", node.pos)) + } else if node.type_ == NODE_REG { + panic(Err("E996: Cannot lock a register", node.pos)) + } + panic(Err("Invalid Expression", node.pos)) +} + func (self *VimLParser) parse_lvaluelist() []*VimNode { var list []*VimNode var node = self.parse_expr() @@ -1744,6 +1896,39 @@ func (self *VimLParser) parse_letlhs() *lhs { return lhs } +// TODO: merge with s:VimLParser.parse_letlhs() ? +func (self *VimLParser) parse_constlhs() *lhs { + var lhs = &lhs{} + var tokenizer = NewExprTokenizer(self.reader) + if tokenizer.peek().type_ == TOKEN_SQOPEN { + tokenizer.get() + for true { + var node = self.parse_lvalue() + lhs.list = append(lhs.list, node) + var token = tokenizer.get() + if token.type_ == TOKEN_SQCLOSE { + break + } else if token.type_ == TOKEN_COMMA { + continue + } else if token.type_ == TOKEN_SEMICOLON { + node = self.parse_lvalue() + lhs.rest = node + token = tokenizer.get() + if token.type_ == TOKEN_SQCLOSE { + break + } else { + panic(Err(viml_printf("E475 Invalid argument: %s", token.value), token.pos)) + } + } else { + panic(Err(viml_printf("E475 Invalid argument: %s", token.value), token.pos)) + } + } + } else { + lhs.left = self.parse_constlvalue() + } + return lhs +} + func (self *VimLParser) ends_excmds(c string) bool { return c == "" || c == "|" || c == "\"" || c == "" || c == "" } @@ -1798,6 +1983,8 @@ func (self *VimLParser) parse_cmd_syntax() { // To find new builtin_commands, run the below script. // $ scripts/update_builtin_commands.sh /path/to/vim/src/ex_cmds.h +// To find new builtin_functions, run the below script. +// $ scripts/update_builtin_functions.sh /path/to/vim/src/evalfunc.c func (self *ExprTokenizer) __init__(reader *StringReader) { self.reader = reader } @@ -1839,6 +2026,14 @@ func (self *ExprTokenizer) get2() *ExprToken { var s = r.getn(3) s += r.read_xdigit() return self.token(TOKEN_NUMBER, s, pos) + } else if c == "0" && (r.p(1) == "B" || r.p(1) == "b") && (r.p(2) == "0" || r.p(2) == "1") { + var s = r.getn(3) + s += r.read_bdigit() + return self.token(TOKEN_NUMBER, s, pos) + } else if c == "0" && (r.p(1) == "Z" || r.p(1) == "z") && r.p(2) != "." { + var s = r.getn(2) + s += r.read_blob() + return self.token(TOKEN_BLOB, s, pos) } else if isdigit(c) { var s = r.read_digit() if r.p(0) == "." && isdigit(r.p(1)) { @@ -1984,9 +2179,14 @@ func (self *ExprTokenizer) get2() *ExprToken { if r.p(1) == "." && r.p(2) == "." { r.seek_cur(3) return self.token(TOKEN_DOTDOTDOT, "...", pos) + } else if r.p(1) == "." { + r.seek_cur(2) + return self.token(TOKEN_DOTDOT, "..", pos) + // TODO check scriptversion? } else { r.seek_cur(1) return self.token(TOKEN_DOT, ".", pos) + // TODO check scriptversion? } } else if c == "*" { r.seek_cur(1) @@ -2007,8 +2207,13 @@ func (self *ExprTokenizer) get2() *ExprToken { r.seek_cur(1) return self.token(TOKEN_COLON, ":", pos) } else if c == "#" { - r.seek_cur(1) - return self.token(TOKEN_SHARP, "#", pos) + if r.p(1) == "{" { + r.seek_cur(2) + return self.token(TOKEN_LITCOPEN, "#{", pos) + } else { + r.seek_cur(1) + return self.token(TOKEN_SHARP, "#", pos) + } } else if c == "(" { r.seek_cur(1) return self.token(TOKEN_POPEN, "(", pos) @@ -2128,6 +2333,31 @@ func (self *ExprTokenizer) get_dstring() string { return s } +func (self *ExprTokenizer) parse_dict_literal_key() *VimNode { + self.reader.skip_white() + var c = self.reader.peek() + if !isalnum(c) && c != "_" && c != "-" { + panic(Err(viml_printf("unexpected character: %s", c), self.reader.getpos())) + } + var node = Node(NODE_STRING) + var s = c + self.reader.seek_cur(1) + node.pos = self.reader.getpos() + for true { + c = self.reader.p(0) + if c == "" || c == "" { + panic(Err("unexpectd EOL", self.reader.getpos())) + } + if !isalnum(c) && c != "_" && c != "-" { + break + } + self.reader.seek_cur(1) + s += c + } + node.value = "'" + s + "'" + return node +} + func (self *ExprParser) __init__(reader *StringReader) { self.reader = reader self.tokenizer = NewExprTokenizer(reader) @@ -2407,6 +2637,7 @@ func (self *ExprParser) parse_expr4() *VimNode { // expr5: expr6 + expr6 .. // expr6 - expr6 .. // expr6 . expr6 .. +// expr6 .. expr6 .. func (self *ExprParser) parse_expr5() *VimNode { var left = self.parse_expr6() for true { @@ -2424,7 +2655,15 @@ func (self *ExprParser) parse_expr5() *VimNode { node.left = left node.right = self.parse_expr6() left = node + } else if token.type_ == TOKEN_DOTDOT { + // TODO check scriptversion? + var node = Node(NODE_CONCAT) + node.pos = token.pos + node.left = left + node.right = self.parse_expr6() + left = node } else if token.type_ == TOKEN_DOT { + // TODO check scriptversion? var node = Node(NODE_CONCAT) node.pos = token.pos node.left = left @@ -2503,6 +2742,9 @@ func (self *ExprParser) parse_expr7() *VimNode { // expr8: expr8[expr1] // expr8[expr1 : expr1] // expr8.name +// expr8->name(expr1, ...) +// expr8->s:user_func(expr1, ...) +// expr8->{lambda}(expr1, ...) // expr8(expr1, ...) func (self *ExprParser) parse_expr8() *VimNode { var left = self.parse_expr9() @@ -2556,35 +2798,29 @@ func (self *ExprParser) parse_expr8() *VimNode { left = node } } + } else if token.type_ == TOKEN_ARROW { + var funcname_or_lambda = self.parse_expr9() + token = self.tokenizer.get() + if token.type_ != TOKEN_POPEN { + panic(Err("E107: Missing parentheses: lambda", token.pos)) + } + var right = Node(NODE_CALL) + right.pos = token.pos + right.left = funcname_or_lambda + right.rlist = self.parse_rlist() + var node = Node(NODE_METHOD) + node.pos = token.pos + node.left = left + node.right = right + left = node } else if token.type_ == TOKEN_POPEN { var node = Node(NODE_CALL) node.pos = token.pos node.left = left - if self.tokenizer.peek().type_ == TOKEN_PCLOSE { - self.tokenizer.get() - } else { - for true { - node.rlist = append(node.rlist, self.parse_expr1()) - token = self.tokenizer.get() - if token.type_ == TOKEN_COMMA { - // XXX: Vim allows foo(a, b, ). Lint should warn it. - if self.tokenizer.peek().type_ == TOKEN_PCLOSE { - self.tokenizer.get() - break - } - } else if token.type_ == TOKEN_PCLOSE { - break - } else { - panic(Err(viml_printf("unexpected token: %s", token.value), token.pos)) - } - } - } - if len(node.rlist) > MAX_FUNC_ARGS { - // TODO: funcname E740: Too many arguments for function: %s - panic(Err("E740: Too many arguments for function", node.pos)) - } + node.rlist = self.parse_rlist() left = node } else if !iswhite(c) && token.type_ == TOKEN_DOT { + // TODO check scriptversion? var node = self.parse_dot(token, left) if node == nil { self.reader.seek_set(pos) @@ -2599,11 +2835,41 @@ func (self *ExprParser) parse_expr8() *VimNode { return left } +func (self *ExprParser) parse_rlist() []*VimNode { + var rlist []*VimNode + var token = self.tokenizer.peek() + if self.tokenizer.peek().type_ == TOKEN_PCLOSE { + self.tokenizer.get() + } else { + for true { + rlist = append(rlist, self.parse_expr1()) + token = self.tokenizer.get() + if token.type_ == TOKEN_COMMA { + // XXX: Vim allows foo(a, b, ). Lint should warn it. + if self.tokenizer.peek().type_ == TOKEN_PCLOSE { + self.tokenizer.get() + break + } + } else if token.type_ == TOKEN_PCLOSE { + break + } else { + panic(Err(viml_printf("unexpected token: %s", token.value), token.pos)) + } + } + } + if len(rlist) > MAX_FUNC_ARGS { + // TODO: funcname E740: Too many arguments for function: %s + panic(Err("E740: Too many arguments for function", token.pos)) + } + return rlist +} + // expr9: number // "string" // 'string' // [expr1, ...] // {expr1: expr1, ...} +// #{literal_key1: expr1, ...} // {args -> expr1} // &option // (expr1) @@ -2621,6 +2887,10 @@ func (self *ExprParser) parse_expr9() *VimNode { node = Node(NODE_NUMBER) node.pos = token.pos node.value = token.value + } else if token.type_ == TOKEN_BLOB { + node = Node(NODE_BLOB) + node.pos = token.pos + node.value = token.value } else if token.type_ == TOKEN_DQUOTE { self.reader.seek_set(pos) node = Node(NODE_STRING) @@ -2656,7 +2926,8 @@ func (self *ExprParser) parse_expr9() *VimNode { } } } - } else if token.type_ == TOKEN_COPEN { + } else if token.type_ == TOKEN_COPEN || token.type_ == TOKEN_LITCOPEN { + var is_litdict = token.type_ == TOKEN_LITCOPEN var savepos = self.reader.tell() var nodepos = token.pos token = self.tokenizer.get() @@ -2673,6 +2944,7 @@ func (self *ExprParser) parse_expr9() *VimNode { // lambda {token,...} {->...} {token->...} node = Node(NODE_LAMBDA) node.pos = nodepos + node.rlist = []*VimNode{} var named = map[string]interface{}{} for true { if token.type_ == TOKEN_ARROW { @@ -2743,7 +3015,13 @@ func (self *ExprParser) parse_expr9() *VimNode { return node } for { - var key = self.parse_expr1() + var key = func() *VimNode { + if is_litdict { + return self.tokenizer.parse_dict_literal_key() + } else { + return self.parse_expr1() + } + }() token = self.tokenizer.get() if token.type_ == TOKEN_CCLOSE { if !viml_empty(node.value) { @@ -2836,6 +3114,31 @@ func (self *ExprParser) parse_dot(token *ExprToken, left *VimNode) *VimNode { return node } +// CONCAT +// str ".." expr6 => (concat str expr6) +func (self *ExprParser) parse_concat(token *ExprToken, left *VimNode) *VimNode { + if left.type_ != NODE_IDENTIFIER && left.type_ != NODE_CURLYNAME && left.type_ != NODE_DICT && left.type_ != NODE_SUBSCRIPT && left.type_ != NODE_CALL && left.type_ != NODE_DOT { + return nil + } + if !iswordc(self.reader.p(0)) { + return nil + } + var pos = self.reader.getpos() + var name = self.reader.read_word() + if isnamec(self.reader.p(0)) { + // XXX: foo is str => ok, foo is obj => invalid expression + // foo.s:bar or foo.bar#baz + return nil + } + var node = Node(NODE_CONCAT) + node.pos = token.pos + node.left = left + node.right = Node(NODE_IDENTIFIER) + node.right.pos = pos + node.right.value = name + return node +} + func (self *ExprParser) parse_identifier() *VimNode { self.reader.skip_white() var npos = self.reader.getpos() @@ -3134,6 +3437,23 @@ func (self *StringReader) read_odigit() string { return r } +func (self *StringReader) read_blob() string { + var r = "" + for { + var s = self.peekn(2) + if viml_eqregh(s, "^[0-9A-Fa-f][0-9A-Fa-f]$") { + r += self.getn(2) + } else if viml_eqregh(s, "^\\.[0-9A-Fa-f]$") { + r += self.getn(1) + } else if viml_eqregh(s, "^[0-9A-Fa-f][^0-9A-Fa-f]$") { + panic(Err("E973: Blob literal should have an even number of hex characters:"+s, self.getpos())) + } else { + break + } + } + return r +} + func (self *StringReader) read_xdigit() string { var r = "" for isxdigit(self.peekn(1)) { @@ -3142,6 +3462,14 @@ func (self *StringReader) read_xdigit() string { return r } +func (self *StringReader) read_bdigit() string { + var r = "" + for self.peekn(1) == "0" || self.peekn(1) == "1" { + r += self.getn(1) + } + return r +} + func (self *StringReader) read_integer() string { var r = "" var c = self.peekn(1) @@ -3169,8 +3497,10 @@ func (self *StringReader) read_white() string { func (self *StringReader) read_nonwhite() string { var r = "" - for !iswhite(self.peekn(1)) { + var ch = self.peekn(1) + for !iswhite(ch) && ch != "" { r += self.getn(1) + ch = self.peekn(1) } return r } @@ -3220,9 +3550,15 @@ func (self *Compiler) compile(node *VimNode) interface{} { } else if node.type_ == NODE_EXCALL { self.compile_excall(node) return nil + } else if node.type_ == NODE_EVAL { + self.compile_eval(node) + return nil } else if node.type_ == NODE_LET { self.compile_let(node) return nil + } else if node.type_ == NODE_CONST { + self.compile_const(node) + return nil } else if node.type_ == NODE_UNLET { self.compile_unlet(node) return nil @@ -3361,10 +3697,14 @@ func (self *Compiler) compile(node *VimNode) interface{} { return self.compile_slice(node) } else if node.type_ == NODE_DOT { return self.compile_dot(node) + } else if node.type_ == NODE_METHOD { + return self.compile_method(node) } else if node.type_ == NODE_CALL { return self.compile_call(node) } else if node.type_ == NODE_NUMBER { return self.compile_number(node) + } else if node.type_ == NODE_BLOB { + return self.compile_blob(node) } else if node.type_ == NODE_STRING { return self.compile_string(node) } else if node.type_ == NODE_LIST { @@ -3387,6 +3727,8 @@ func (self *Compiler) compile(node *VimNode) interface{} { return self.compile_curlynameexpr(node) } else if node.type_ == NODE_LAMBDA { return self.compile_lambda(node) + } else if node.type_ == NODE_HEREDOC { + return self.compile_heredoc(node) } else if node.type_ == NODE_PARENEXPR { return self.compile_parenexpr(node) } else { @@ -3423,14 +3765,31 @@ func (self *Compiler) compile_function(node *VimNode) { } return ss }() - if !viml_empty(rlist) && rlist[len(rlist)-1] == "..." { - rlist[len(rlist)-1] = ". ..." - } - if viml_empty(rlist) { - self.out("(function (%s)", left) - } else { - self.out("(function (%s %s)", left, viml_join(rlist, " ")) + var default_args = func() []string { + var ss []string + for _, vval := range node.default_args { + ss = append(ss, self.compile(vval).(string)) + } + return ss + }() + if !viml_empty(rlist) { + var remaining = false + if rlist[len(rlist)-1] == "..." { + rlist = rlist[:len(rlist)-1] + remaining = true + } + for _, i := range viml_range(0, len(rlist)-1) { + if i < len(rlist)-len(default_args) { + left += viml_printf(" %s", rlist[i]) + } else { + left += viml_printf(" (%s %s)", rlist[i], default_args[i+len(default_args)-len(rlist)]) + } + } + if remaining { + left += " . ..." + } } + self.out("(function (%s)", left) self.incindent(" ") self.compile_body(node.body) self.out(")") @@ -3453,6 +3812,10 @@ func (self *Compiler) compile_excall(node *VimNode) { self.out("(call %s)", self.compile(node.left).(string)) } +func (self *Compiler) compile_eval(node *VimNode) { + self.out("(eval %s)", self.compile(node.left).(string)) +} + func (self *Compiler) compile_let(node *VimNode) { var left = "" if node.left != nil { @@ -3474,6 +3837,28 @@ func (self *Compiler) compile_let(node *VimNode) { self.out("(let %s %s %s)", node.op, left, right) } +// TODO: merge with s:Compiler.compile_let() ? +func (self *Compiler) compile_const(node *VimNode) { + var left = "" + if node.left != nil { + left = self.compile(node.left).(string) + } else { + left = viml_join(func() []string { + var ss []string + for _, vval := range node.list { + ss = append(ss, self.compile(vval).(string)) + } + return ss + }(), " ") + if node.rest != nil { + left += " . " + self.compile(node.rest).(string) + } + left = "(" + left + ")" + } + var right = self.compile(node.right) + self.out("(const %s %s %s)", node.op, left, right) +} + func (self *Compiler) compile_unlet(node *VimNode) { var list = func() []string { var ss []string @@ -3862,6 +4247,10 @@ func (self *Compiler) compile_dot(node *VimNode) string { return viml_printf("(dot %s %s)", self.compile(node.left).(string), self.compile(node.right)) } +func (self *Compiler) compile_method(node *VimNode) string { + return viml_printf("(method %s %s)", self.compile(node.left).(string), self.compile(node.right)) +} + func (self *Compiler) compile_call(node *VimNode) string { var rlist = func() []string { var ss []string @@ -3881,6 +4270,10 @@ func (self *Compiler) compile_number(node *VimNode) string { return node.value.(string) } +func (self *Compiler) compile_blob(node *VimNode) string { + return node.value.(string) +} + func (self *Compiler) compile_string(node *VimNode) string { return node.value.(string) } @@ -3905,6 +4298,23 @@ func (self *Compiler) compile_curlynamepart(node *VimNode) string { return node.value.(string) } +func (self *Compiler) escape_string(str string) string { + var out = "\"" + for _, c := range viml_split(str, "\\zs") { + if c == "\n" { + out += "\\n" + } else if c == "\t" { + out += "\\t" + } else if c == "\r" { + out += "\\r" + } else { + out += c + } + } + out += "\"" + return out +} + func (self *Compiler) compile_lambda(node *VimNode) string { var rlist = func() []string { var ss []string @@ -3916,4 +4326,35 @@ func (self *Compiler) compile_lambda(node *VimNode) string { return viml_printf("(lambda (%s) %s)", viml_join(rlist, " "), self.compile(node.left).(string)) } +func (self *Compiler) compile_heredoc(node *VimNode) string { + var rlist = func() string { + if viml_empty(node.rlist) { + return "(list)" + } else { + return "(list " + viml_join(func() []string { + var ss []string + for _, vval := range node.rlist { + ss = append(ss, self.escape_string(vval.value.(string))) + } + return ss + }(), " ") + ")" + } + }() + var body = func() string { + if viml_empty(node.body) { + return "(list)" + } else { + return "(list " + viml_join(func() []string { + var ss []string + for _, vval := range node.body { + ss = append(ss, self.escape_string(vval.value.(string))) + } + return ss + }(), " ") + ")" + } + }() + var op = self.escape_string(node.op) + return viml_printf("(heredoc %s %s %s)", rlist, op, body) +} + // TODO: under construction diff --git a/printer/opprec.go b/printer/opprec.go index c2c7c89..483078e 100644 --- a/printer/opprec.go +++ b/printer/opprec.go @@ -61,9 +61,9 @@ func opprec(op ast.Expr) int { default: panic(fmt.Errorf("unexpected token of UnaryExpr: %v", n.Op)) } - case *ast.SubscriptExpr, *ast.SliceExpr, *ast.CallExpr, *ast.DotExpr: + case *ast.SubscriptExpr, *ast.SliceExpr, *ast.CallExpr, *ast.DotExpr, *ast.MethodExpr: return 8 - case *ast.BasicLit, *ast.Ident, *ast.List, *ast.Dict, *ast.CurlyName: + case *ast.BasicLit, *ast.Ident, *ast.List, *ast.Dict, *ast.CurlyName, *ast.HeredocExpr: return 9 case *ast.CurlyNameExpr, *ast.CurlyNameLit, *ast.LambdaExpr: panic(fmt.Errorf("precedence is undefined for expr: %T", n)) diff --git a/test/node_position/test_position.ok b/test/node_position/test_position.ok new file mode 100644 index 0000000..f8d5620 --- /dev/null +++ b/test/node_position/test_position.ok @@ -0,0 +1,10 @@ +function! F() + let x = +\ 1 + + let x = " + \1 + \2 <- tab + \3 マルチバイト + \4" +endfunction diff --git a/test/node_position/test_position.vim b/test/node_position/test_position.vim new file mode 100644 index 0000000..616e3d4 --- /dev/null +++ b/test/node_position/test_position.vim @@ -0,0 +1,43 @@ +let s:vimlparser = vimlparser#import() + +function! s:run() + let src = [ + \ '', + \ 'function! F()', + \ ' let x =', + \ '\ 1', + \ '', + \ ' let x = "', + \ ' \1', + \ ' \2 <- tab', + \ ' \3 マルチバイト', + \ ' \4"', + \ 'endfunction', + \ '', + \ '" END', + \] + let r = s:vimlparser.StringReader.new(src) + let p = s:vimlparser.VimLParser.new(0) + let c = s:vimlparser.Compiler.new() + let toplevel = p.parse(r) + let func = toplevel.body[0] + let body = s:extract_body(func, src) + call writefile(split(body, "\n"), 'test/node_position/test_position.out') + qall! +endfunction + +function! s:extract_body(func, src) + let pos = a:func.pos + + " FIXME calculating endpos is workaround. Ideally, it should have the end + " position of the node. + + let endpos = a:func.endfunction.pos + let endfunc = a:func.endfunction.ea + let cmdlen = endfunc.argpos.offset - endfunc.cmdpos.offset + let endpos.offset += cmdlen + + return join(a:src, "\n")[pos.offset : endpos.offset] +endfunction + +call s:run() diff --git a/test/run.bat b/test/run.bat index 267e325..a2d81a7 100644 --- a/test/run.bat +++ b/test/run.bat @@ -1,2 +1,5 @@ @echo off -vim -u NONE -N --cmd "let &rtp .= ',' . getcwd()" -S test/run.vim +vim -Nu test/vimrc -S test/run.vim +set EXIT=%ERRORLEVEL% +if exist test.log type test.log +exit /b %EXIT% diff --git a/test/run.sh b/test/run.sh index b94c403..2a3f08d 100755 --- a/test/run.sh +++ b/test/run.sh @@ -1,2 +1,5 @@ #!/bin/sh -vim -u NONE -N --cmd "let &rtp .= ',' . getcwd()" -S test/run.vim +vim -Nu test/vimrc -S test/run.vim +EXIT=$? +[ -e test.log ] && cat test.log +exit $EXIT diff --git a/test/run.vim b/test/run.vim index c7d657f..bd7c029 100644 --- a/test/run.vim +++ b/test/run.vim @@ -1,13 +1,15 @@ - - let s:vimlparser = vimlparser#import() let s:sdir = expand(':p:h') function! s:run() + enew + set buftype=nofile + let ng = 0 for vimfile in glob(s:sdir . '/test*.vim', 0, 1) let okfile = fnamemodify(vimfile, ':r') . '.ok' let outfile = fnamemodify(vimfile, ':r') . '.out' + let skip = filereadable(fnamemodify(vimfile, ':r') . '.skip') let src = readfile(vimfile) let r = s:vimlparser.StringReader.new(src) if vimfile =~# 'test_neo' @@ -23,13 +25,28 @@ function! s:run() catch call writefile([v:exception], outfile) endtry - if system(printf('diff %s %s', shellescape(okfile), shellescape(outfile))) == "" + let diff = system(printf('diff -u %s %s', shellescape(okfile), shellescape(outfile))) + if empty(diff) let line = printf('%s => ok', fnamemodify(vimfile, ':.')) + call append(line('$'), line) else - let line = printf('%s => ng', fnamemodify(vimfile, ':.')) + if !skip + let ng += 1 + endif + let line = printf('%s => ' . (skip ? 'skip' : 'ng'), fnamemodify(vimfile, ':.')) + call append(line('$'), line) + for line in split(diff, '\n') + call append(line('$'), ' ' . line) + endfor endif - call append(line('$'), line) endfor + if $CI == 'true' + call writefile(getline(1, '$'), 'test.log') + if ng == 0 + quit! + endif + cquit! + endif syntax enable match Error /^.* => ng$/ endfunction diff --git a/test/run_command.sh b/test/run_command.sh new file mode 100755 index 0000000..2a079d8 --- /dev/null +++ b/test/run_command.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +if [ $# -eq 0 ]; then + echo "USAGE: ./test/run_command.sh " + echo " Example: ./test/run_command.sh python3 py/vimlparser.py" + exit 1 +fi +vimlparser=$@ + +exit_status=0 + +test_file() { + local vimfile=$1 + local base=$(basename "$vimfile" ".vim") + local okfile="test/${base}.ok" + local outfile="test/${base}.out" + + if [[ -f "test/${base}.skip" ]]; then + return + fi + + local neovim="" + if [[ $vimfile =~ "test_neo" ]]; then + neovim="--neovim" + fi + + rm -f ${outfile} + + ${vimlparser} ${neovim} ${vimfile} &> ${outfile} + + diffout=$(diff -u ${outfile} ${okfile}) + if [ -n "$diffout" ]; then + exit_status=1 + echo "${diffout}" + fi +} + +for f in test/test*.vim; do + test_file ${f} +done + +exit $exit_status diff --git a/test/test1.ok b/test/test1.ok index e6e3bd0..7cc25a6 100644 --- a/test/test1.ok +++ b/test/test1.ok @@ -1,6 +1,10 @@ ; test1 (function (s:foo a b . ...) (return 0)) +(function (s:bar (a 1) (b 2) . ...) + (return 0)) +(function (s:baz a (b 2) . ...) + (return 0)) (if 1 (echo "if 1") elseif 2 @@ -14,6 +18,8 @@ (echo a b c)) (delfunction s:foo) (call (s:foo 1 2 3)) +(eval (filter odds 'v:val % 2')) +(eval 42) (let = a (dict ("x" "y"))) (let = (a b . c) (list 1 2 3)) (let += (a b . c) (list 1 2 3)) @@ -46,3 +52,16 @@ (echo (subscript x 0) (subscript x y)) (echo (slice x 1 2) (slice x 1 nil) (slice x nil 2) (slice x nil nil)) (echo (dot x y) (dot (dot x y) z)) +(let = a 1) +(let += a 2) +(let *= a 3) +(let /= a 4) +(let %= a 5) +(let ..= a 'foo') +(echo (concat (concat 'foo' 'bar') 'baz')) +(echo (concat 'foo' (concat 'bar' 'baz'))) +(echo (concat (concat 'foo' 'bar') 'baz')) +(let = a '🐥') +(const = a 1) +(const = (a b) (list 1 2)) +(const = (a b . c) (list 1 2 3)) diff --git a/test/test1.vim b/test/test1.vim index a237e5d..b5bc331 100644 --- a/test/test1.vim +++ b/test/test1.vim @@ -2,6 +2,12 @@ function s:foo(a, b, ...) return 0 endfunction +function s:bar(a = 1, b = 2, ...) + return 0 +endfunction +function s:baz(a, b = 2, ...) + return 0 +endfunction if 1 echo "if 1" elseif 2 @@ -18,6 +24,8 @@ for [a, b; c] in d endfor delfunction s:foo call s:foo(1, 2, 3) +eval filter(odds, 'v:val % 2') +eval 42 let a = {"x": "y"} let [a, b; c] = [1, 2, 3] let [a, b; c] += [1, 2, 3] @@ -51,3 +59,16 @@ echo {} {"x":"y"} {"x":"y","z":"w",} echo x[0] x[y] echo x[1:2] x[1:] x[:2] x[:] echo x.y x.y.z +let a = 1 +let a += 2 +let a *= 3 +let a /= 4 +let a %= 5 +let a ..= 'foo' +echo ('foo' .. 'bar')..'baz' +echo 'foo' .. ('bar'..'baz') +echo 'foo' .. 'bar' .. 'baz' +let a = '🐥' +const a = 1 +const [a, b] = [1, 2] +const [a, b; c] = [1, 2, 3] diff --git a/test/test_blob.ok b/test/test_blob.ok new file mode 100644 index 0000000..ea3d2c6 --- /dev/null +++ b/test/test_blob.ok @@ -0,0 +1,8 @@ +(echo 0z) +(echo 0z11) +(echo 0zdeadbeef) +(echo 0Zdeadbeef) +(echo 0zDEADBEEF) +(echo 0ZDEADBEEF) +(echo 0ZDEAD.BEEF) +(echo (+ 0z00 0zFF)) diff --git a/test/test_blob.vim b/test/test_blob.vim new file mode 100644 index 0000000..f9b3274 --- /dev/null +++ b/test/test_blob.vim @@ -0,0 +1,8 @@ +echo 0z +echo 0z11 +echo 0zdeadbeef +echo 0Zdeadbeef +echo 0zDEADBEEF +echo 0ZDEADBEEF +echo 0ZDEAD.BEEF +echo 0z00 + 0zFF diff --git a/test/test_command.ok b/test/test_command.ok index 415661e..2d1a6fd 100644 --- a/test/test_command.ok +++ b/test/test_command.ok @@ -3,3 +3,4 @@ (excmd "normal gg") (excmd "Usercmd x, y z 123") (excmd "Usercmd! with bang") +(excmd "write ++enc=utf-8") diff --git a/test/test_command.vim b/test/test_command.vim index 65d8eb4..3858de8 100644 --- a/test/test_command.vim +++ b/test/test_command.vim @@ -3,3 +3,4 @@ normal! gg normal gg Usercmd x, y z 123 Usercmd! with bang +write ++enc=utf-8 diff --git a/test/test_delfunction.ok b/test/test_delfunction.ok new file mode 100644 index 0000000..e7828d9 --- /dev/null +++ b/test/test_delfunction.ok @@ -0,0 +1,2 @@ +(delfunction F) +(delfunction F) diff --git a/test/test_delfunction.vim b/test/test_delfunction.vim new file mode 100644 index 0000000..fbd9129 --- /dev/null +++ b/test/test_delfunction.vim @@ -0,0 +1,2 @@ +delfunction F +delfunction! F diff --git a/test/test_err_blob.ok b/test/test_err_blob.ok new file mode 100644 index 0000000..74b3dda --- /dev/null +++ b/test/test_err_blob.ok @@ -0,0 +1 @@ +vimlparser: E973: Blob literal should have an even number of hex characters:0X: line 2 col 8 diff --git a/test/test_err_blob.vim b/test/test_err_blob.vim new file mode 100644 index 0000000..2c715f3 --- /dev/null +++ b/test/test_err_blob.vim @@ -0,0 +1,4 @@ +echo 0z0 +echo 0z0X +echo 0zAABBC +echo 0zAAB.BCC diff --git a/test/test_err_const1.ok b/test/test_err_const1.ok new file mode 100644 index 0000000..70f9c97 --- /dev/null +++ b/test/test_err_const1.ok @@ -0,0 +1 @@ +vimlparser: E996: Cannot lock a list or dict: line 2 col 8 diff --git a/test/test_err_const1.vim b/test/test_err_const1.vim new file mode 100644 index 0000000..a796248 --- /dev/null +++ b/test/test_err_const1.vim @@ -0,0 +1,2 @@ +let a = [0] +const a[0] = 1 diff --git a/test/test_err_const2.ok b/test/test_err_const2.ok new file mode 100644 index 0000000..70f9c97 --- /dev/null +++ b/test/test_err_const2.ok @@ -0,0 +1 @@ +vimlparser: E996: Cannot lock a list or dict: line 2 col 8 diff --git a/test/test_err_const2.vim b/test/test_err_const2.vim new file mode 100644 index 0000000..307e6c7 --- /dev/null +++ b/test/test_err_const2.vim @@ -0,0 +1,2 @@ +let a = {} +const a["b"] = 1 diff --git a/test/test_err_const3.ok b/test/test_err_const3.ok new file mode 100644 index 0000000..70f9c97 --- /dev/null +++ b/test/test_err_const3.ok @@ -0,0 +1 @@ +vimlparser: E996: Cannot lock a list or dict: line 2 col 8 diff --git a/test/test_err_const3.vim b/test/test_err_const3.vim new file mode 100644 index 0000000..6fbf109 --- /dev/null +++ b/test/test_err_const3.vim @@ -0,0 +1,2 @@ +let a = {} +const a.b = 1 diff --git a/test/test_err_const4.ok b/test/test_err_const4.ok new file mode 100644 index 0000000..6875a74 --- /dev/null +++ b/test/test_err_const4.ok @@ -0,0 +1 @@ +vimlparser: E996: Cannot lock an environment variable: line 1 col 7 diff --git a/test/test_err_const4.vim b/test/test_err_const4.vim new file mode 100644 index 0000000..aceb1f3 --- /dev/null +++ b/test/test_err_const4.vim @@ -0,0 +1 @@ +const $a = 1 diff --git a/test/test_err_const5.ok b/test/test_err_const5.ok new file mode 100644 index 0000000..800eb4a --- /dev/null +++ b/test/test_err_const5.ok @@ -0,0 +1 @@ +vimlparser: E996: Cannot lock a register: line 1 col 7 diff --git a/test/test_err_const5.vim b/test/test_err_const5.vim new file mode 100644 index 0000000..9222d9d --- /dev/null +++ b/test/test_err_const5.vim @@ -0,0 +1 @@ +const @a = 1 diff --git a/test/test_err_const6.ok b/test/test_err_const6.ok new file mode 100644 index 0000000..ac0c7b3 --- /dev/null +++ b/test/test_err_const6.ok @@ -0,0 +1 @@ +vimlparser: E996: Cannot lock an option: line 1 col 7 diff --git a/test/test_err_const6.vim b/test/test_err_const6.vim new file mode 100644 index 0000000..a0dd430 --- /dev/null +++ b/test/test_err_const6.vim @@ -0,0 +1 @@ +const &encoding = "foo" diff --git a/test/test_err_funcarg_default.ok b/test/test_err_funcarg_default.ok new file mode 100644 index 0000000..fa80e1f --- /dev/null +++ b/test/test_err_funcarg_default.ok @@ -0,0 +1 @@ +vimlparser: E989: Non-default argument follows default argument: line 1 col 21 diff --git a/test/test_err_funcarg_default.vim b/test/test_err_funcarg_default.vim new file mode 100644 index 0000000..8dddfae --- /dev/null +++ b/test/test_err_funcarg_default.vim @@ -0,0 +1,2 @@ +function Foo(abc=1, xyz) +endfunction diff --git a/test/test_err_method.ok b/test/test_err_method.ok new file mode 100644 index 0000000..935f777 --- /dev/null +++ b/test/test_err_method.ok @@ -0,0 +1 @@ +vimlparser: E107: Missing parentheses: lambda: line 1 col 32 diff --git a/test/test_err_method.vim b/test/test_err_method.vim new file mode 100644 index 0000000..734380d --- /dev/null +++ b/test/test_err_method.vim @@ -0,0 +1 @@ +echo 'foo'->{s -> s .. ' bar' } diff --git a/test/test_err_toomanyarg.ok b/test/test_err_toomanyarg.ok index 19cac26..9791da7 100644 --- a/test/test_err_toomanyarg.ok +++ b/test/test_err_toomanyarg.ok @@ -1 +1 @@ -vimlparser: E740: Too many arguments for function: line 1 col 9 +vimlparser: E740: Too many arguments for function: line 1 col 83 diff --git a/test/test_funcarg_default.ok b/test/test_funcarg_default.ok new file mode 100644 index 0000000..0eebb8a --- /dev/null +++ b/test/test_funcarg_default.ok @@ -0,0 +1,2 @@ +(function (X (foo (+ 1 2))) + (return a:foo)) diff --git a/test/test_funcarg_default.vim b/test/test_funcarg_default.vim new file mode 100644 index 0000000..fa9deb6 --- /dev/null +++ b/test/test_funcarg_default.vim @@ -0,0 +1,3 @@ +function X(foo=1+2) + return a:foo +endfunction diff --git a/test/test_funcname.ok b/test/test_funcname.ok new file mode 100644 index 0000000..d37d05e --- /dev/null +++ b/test/test_funcname.ok @@ -0,0 +1,3 @@ +(function (_F)) +(function (s:SID)) +(function (SID)) diff --git a/test/test_funcname.vim b/test/test_funcname.vim new file mode 100644 index 0000000..b4011d2 --- /dev/null +++ b/test/test_funcname.vim @@ -0,0 +1,8 @@ +function _F() +endfunction + +function s:SID() +endfunction + +function SID() +endfunction diff --git a/test/test_heredoc.ok b/test/test_heredoc.ok new file mode 100644 index 0000000..0de3f6b --- /dev/null +++ b/test/test_heredoc.ok @@ -0,0 +1,3 @@ +(let =<< a (heredoc (list) "EOS" (list "hello" " world"))) +(let =<< a (heredoc (list "trim") "EOS" (list "\thello" "world"))) +(let =<< a (heredoc (list) "EOS" (list))) diff --git a/test/test_heredoc.vim b/test/test_heredoc.vim new file mode 100644 index 0000000..e92322b --- /dev/null +++ b/test/test_heredoc.vim @@ -0,0 +1,10 @@ +let a =<< EOS +hello + world +EOS +let a =<< trim EOS + hello +world +EOS +let a =<< EOS +EOS diff --git a/test/test_litdict.ok b/test/test_litdict.ok new file mode 100644 index 0000000..72b9d98 --- /dev/null +++ b/test/test_litdict.ok @@ -0,0 +1,8 @@ +(echo (dict)) +(echo (dict ('foo-bar' 42))) +(echo (dict ('foo-' 42))) +(echo (dict ('-bar' 42))) +(echo (dict ('1-1' 0))) +(echo (dict ('one' 1) ('two2' 2) ('3three' 3) ('44' 4))) +(echo (dict ('x' (dict)))) +(echo (list (dict))) diff --git a/test/test_litdict.vim b/test/test_litdict.vim new file mode 100644 index 0000000..9a4e005 --- /dev/null +++ b/test/test_litdict.vim @@ -0,0 +1,8 @@ +echo #{} +echo #{foo-bar: 42} +echo #{foo-: 42} +echo #{-bar: 42} +echo #{1-1:0} +echo #{one: 1, two2: 2, 3three: 3, 44: 4} +echo #{x : {},} +echo [#{},] diff --git a/test/test_method.ok b/test/test_method.ok new file mode 100644 index 0000000..7100811 --- /dev/null +++ b/test/test_method.ok @@ -0,0 +1,11 @@ +(echo (method 'alice' (s:hello))) +(echo (method 'alice' (s:hello 'bob'))) +(echo (method (list 1 2 3) (map (lambda (i n) (* n 2))))) +(echo (method 'john' ((lambda (s) (concat 'hello ' s))))) +(echo (method 'john' ((lambda (s b) (concat (concat (concat (concat 'hello ' s) ' ... and goodbye ') b) '!')) 'bob'))) +(echo (method 'john' ((lambda (...) (concat 'hello ' (method a:000 (join ', ')))) 'bob'))) +(eval (method funcs (map (lambda () (v:val))))) +(let = session (method (method (s:get_sessions) (filter (lambda () (==# (dot v:val name) session_name)))) (get 0 (dict)))) +(let = session (method (method (s:get_sessions) (filter (lambda () (==# (dot v:val name) session_name)))) (get 0 (dict)))) +(echo (method expr8 (s:flatmap))) +(echo (method (method expr8 (s:flatmap)) (filter (lambda (i) (% i 2))))) diff --git a/test/test_method.vim b/test/test_method.vim new file mode 100644 index 0000000..df27744 --- /dev/null +++ b/test/test_method.vim @@ -0,0 +1,11 @@ +echo 'alice'->s:hello() +echo 'alice'->s:hello('bob') +echo [1,2,3]->map({i,n -> n * 2}) +echo 'john'->{s -> 'hello ' .. s }() +echo 'john'->{s,b -> 'hello ' .. s .. ' ... and goodbye ' .. b .. '!' }('bob') +echo 'john'->{... -> 'hello ' .. a:000->join(', ') }('bob') +eval funcs->map({-> v:val() }) +let session = s:get_sessions()->filter({-> v:val.name ==# session_name })->get(0, {}) +let session = (s:get_sessions()->filter({-> v:val.name ==# session_name }))->get(0, {}) +echo expr8->s:flatmap() +echo expr8->s:flatmap()->filter({i -> i % 2}) diff --git a/test/test_modifier_commands.ok b/test/test_modifier_commands.ok new file mode 100644 index 0000000..abfa727 --- /dev/null +++ b/test/test_modifier_commands.ok @@ -0,0 +1,2 @@ +(call (Func)) +(call (Func)) diff --git a/test/test_modifier_commands.vim b/test/test_modifier_commands.vim new file mode 100644 index 0000000..d05c626 --- /dev/null +++ b/test/test_modifier_commands.vim @@ -0,0 +1,6 @@ +aboveleft belowright botright browse confirm hide keepalt keepjumps + \ keepmarks keeppatterns lockmarks noswapfile silent tab + \ topleft verbose vertical call Func() +abo bel bo bro conf hid keepalt keepj + \ kee keepp loc nos sil tab + \ to verb vert call Func() diff --git a/test/test_number.ok b/test/test_number.ok index ce0839f..ad637fa 100644 --- a/test/test_number.ok +++ b/test/test_number.ok @@ -2,7 +2,13 @@ (echo 1.0) (echo 1.23) (echo 0xdeadbeef) +(echo 0Xdeadbeef) (echo 033) (echo 1.2e-3) (echo 4.5E+67) (echo 4.5e8) +(echo 0b1011) +(echo 0b0) +(echo 0B1011) +(echo 0B0) +(echo 0b01 23) diff --git a/test/test_number.vim b/test/test_number.vim index 3b2c884..1e6e42c 100644 --- a/test/test_number.vim +++ b/test/test_number.vim @@ -2,7 +2,13 @@ echo 1 echo 1.0 echo 1.23 echo 0xdeadbeef +echo 0Xdeadbeef echo 033 echo 1.2e-3 echo 4.5E+67 echo 4.5e8 +echo 0b1011 +echo 0b0 +echo 0B1011 +echo 0B0 +echo 0b0123 diff --git a/test/test_xxx_colonsharp.skip b/test/test_xxx_colonsharp.skip new file mode 100644 index 0000000..e69de29 diff --git a/test/vimrc b/test/vimrc new file mode 100644 index 0000000..e98989a --- /dev/null +++ b/test/vimrc @@ -0,0 +1,6 @@ +let &rtp .= ',' . getcwd() + +if $TEST_PROFILE != '' + execute 'profile' 'start' $TEST_PROFILE + profile! file ./autoload/* +endif diff --git a/token/token.go b/token/token.go index 409d46b..ba38399 100644 --- a/token/token.go +++ b/token/token.go @@ -63,6 +63,7 @@ const ( CCLOSE COMMA NUMBER + BLOB SQUOTE DQUOTE OPTION @@ -134,6 +135,7 @@ var tokens = [...]string{ CCLOSE: "}", COMMA: ",", NUMBER: "", + BLOB: "", SQUOTE: "'", DQUOTE: `"`, OPTION: "<&OPTION>",