Skip to content

Commit

Permalink
Merge pull request #27 from haya14busa/vim-vimlparser-lambda
Browse files Browse the repository at this point in the history
support lambda
  • Loading branch information
mattn authored May 25, 2017
2 parents 1f8c36b + a0f43a0 commit b5f8373
Show file tree
Hide file tree
Showing 13 changed files with 175 additions and 29 deletions.
153 changes: 124 additions & 29 deletions autoload/vimlparser.vim
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ let s:NODE_ENV = 88
let s:NODE_REG = 89
let s:NODE_CURLYNAMEPART = 90
let s:NODE_CURLYNAMEEXPR = 91
let s:NODE_LAMBDA = 92

let s:TOKEN_EOF = 1
let s:TOKEN_EOL = 2
Expand Down Expand Up @@ -199,6 +200,7 @@ let s:TOKEN_SEMICOLON = 61
let s:TOKEN_BACKTICK = 62
let s:TOKEN_DOTDOTDOT = 63
let s:TOKEN_SHARP = 64
let s:TOKEN_ARROW = 65

let s:MAX_FUNC_ARGS = 20

Expand Down Expand Up @@ -398,6 +400,7 @@ endfunction
" REG .value
" CURLYNAMEPART .value
" CURLYNAMEEXPR .value
" LAMBDA .rlist .left
function! s:Node(type)
return {'type': a:type}
endfunction
Expand Down Expand Up @@ -2568,8 +2571,13 @@ function! s:ExprTokenizer.get2()
call r.seek_cur(1)
return self.token(s:TOKEN_PLUS, '+', pos)
elseif c ==# '-'
call r.seek_cur(1)
return self.token(s:TOKEN_MINUS, '-', pos)
if r.p(1) ==# '>'
call r.seek_cur(2)
return self.token(s:TOKEN_ARROW, '->', pos)
else
call r.seek_cur(1)
return self.token(s:TOKEN_MINUS, '-', pos)
endif
elseif c ==# '.'
if r.p(1) ==# '.' && r.p(2) ==# '.'
call r.seek_cur(3)
Expand Down Expand Up @@ -3200,6 +3208,7 @@ endfunction
" 'string'
" [expr1, ...]
" {expr1: expr1, ...}
" {args -> expr1}
" &option
" (expr1)
" variable
Expand Down Expand Up @@ -3251,42 +3260,121 @@ function! s:ExprParser.parse_expr9()
endwhile
endif
elseif token.type == s:TOKEN_COPEN
let node = s:Node(s:NODE_DICT)
let node.pos = token.pos
let node.value = []
let token = self.tokenizer.peek()
if token.type == s:TOKEN_CCLOSE
call self.tokenizer.get()
else
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)
" 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
endif
" fallback to dict or {expr} if true
let fallback = 0
if lambda
" lambda {token,...} {->...} {token->...}
let node = s:Node(s:NODE_LAMBDA)
let node.pos = nodepos
let node.rlist = []
let named = {}
while 1
let key = self.parse_expr1()
let token = self.tokenizer.get()
if token.type == s:TOKEN_CCLOSE
if !empty(node.value)
throw s:Err(printf('unexpected token: %s', token.value), token.pos)
endif
call self.reader.seek_set(pos)
let node = self.parse_identifier()
if token.type == s:TOKEN_ARROW
break
endif
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
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)
throw s:Err(printf('E853: Duplicate argument name: %s', token.value), token.pos)
endif
let named[token.value] = 1
let varnode = s:Node(s:NODE_IDENTIFIER)
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
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
" XXX: Vim allows last comma. {a, b, -> ...} => OK
let token = self.tokenizer.peek()
if token.type == s:TOKEN_ARROW
call self.tokenizer.get()
break
endif
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
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
call self.tokenizer.get()
break
else
throw s:Err(printf('unexpected token: %s', token.value), token.pos)
endif
elseif token.type == s:TOKEN_CCLOSE
break
else
throw s:Err(printf('unexpected token: %s', token.value), token.pos)
let fallback = 1
break
endif
let token = self.tokenizer.get()
endwhile
if !fallback
let node.left = self.parse_expr1()
let token = self.tokenizer.get()
if token.type != s:TOKEN_CCLOSE
throw s:Err(printf('unexpected token: %s', token.value), token.pos)
endif
return node
endif
endif
" dict
let node = s:Node(s:NODE_DICT)
let node.pos = nodepos
let node.value = []
call self.reader.seek_set(savepos)
let token = self.tokenizer.peek()
if token.type == s:TOKEN_CCLOSE
call self.tokenizer.get()
return node
endif
while 1
let key = self.parse_expr1()
let token = self.tokenizer.get()
if token.type == s:TOKEN_CCLOSE
if !empty(node.value)
throw s:Err(printf('unexpected token: %s', token.value), token.pos)
endif
call self.reader.seek_set(pos)
let node = self.parse_identifier()
break
endif
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
call self.tokenizer.get()
break
endif
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
let node = self.parse_expr1()
let token = self.tokenizer.get()
Expand Down Expand Up @@ -3955,6 +4043,8 @@ function! s:Compiler.compile(node)
return self.compile_curlynamepart(a:node)
elseif a:node.type == s:NODE_CURLYNAMEEXPR
return self.compile_curlynameexpr(a:node)
elseif a:node.type == s:NODE_LAMBDA
return self.compile_lambda(a:node)
else
throw printf('Compiler: unknown node: %s', string(a:node))
endif
Expand Down Expand Up @@ -4410,6 +4500,11 @@ function! s:Compiler.compile_curlynameexpr(node)
return '{' . self.compile(a:node.value) . '}'
endfunction

function! s:Compiler.compile_lambda(node)
let rlist = map(a:node.rlist, 'self.compile(v:val)')
return printf('(lambda (%s) %s)', join(rlist, ' '), self.compile(a:node.left))
endfunction

" TODO: under construction
let s:RegexpParser = {}

Expand Down
11 changes: 11 additions & 0 deletions test/test_dict.ok
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
; test_dict
(echo (dict))
(echo (dict ('1' 1)))
(echo (dict (1 1)))
(echo (dict (x 1)))
(echo (dict ("\<cr>" 1)))
(echo (dict ("vim" 1)))
(echo (dict ((+ 1 1) 2)))
; XXX: echo {x:1}
(echo (dict ('x' (dict))))
(echo (list (dict)))
11 changes: 11 additions & 0 deletions test/test_dict.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
" test_dict
echo {}
echo {'1': 1}
echo {1: 1}
echo {x : 1}
echo {"\<cr>": 1}
echo {"vim": 1}
echo {1+1:2}
" XXX: echo {x:1}
echo {'x' : {},}
echo [{},]
1 change: 1 addition & 0 deletions test/test_err_lambdaarg.ok
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vimlparser: E125: Illegal argument: a:bar: line 1 col 37
1 change: 1 addition & 0 deletions test/test_err_lambdaarg.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
echo {a, b, C, d_e, _f, g_, h0, i1, a:bar -> 1}
1 change: 1 addition & 0 deletions test/test_err_lambdaarg_duplicate.ok
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vimlparser: E853: Duplicate argument name: b: line 1 col 13
1 change: 1 addition & 0 deletions test/test_err_lambdaarg_duplicate.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
echo {a, b, b, c ->1}
5 changes: 5 additions & 0 deletions test/test_lambda.ok
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
; test_lambda
(echo (lambda () (+ 1 1)))
(echo (lambda (i v) (>= v s:x)))
(echo (lambda (...) a:000))
(echo ((lambda (x) (* x 2)) 14))
5 changes: 5 additions & 0 deletions test/test_lambda.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
" test_lambda
echo {->1 + 1}
echo {i, v -> v >= s:x}
echo {...->a:000}
echo {x->x*2}(14)
1 change: 1 addition & 0 deletions test/test_xxx_err_lambdaarg_space_comma.ok
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vimlparser: E475: Invalid argument: White space is not allowed before comma: line 10 col 8
10 changes: 10 additions & 0 deletions test/test_xxx_err_lambdaarg_space_comma.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
echo {a->'OK1'}
echo { a->'OK2'}
echo {a ->'OK3'}
echo { a ->'OK4'}
echo {a,b->'OK5'}
echo {a, b->'OK6'}
echo { a, b->'OK7'}
echo {a, b ->'OK8'}
echo { a, b ->'OK9'}
echo {a ,b->'NG'}
2 changes: 2 additions & 0 deletions test/test_xxx_lambdaarg_last_comma.ok
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
(echo (lambda (a) 0))
(echo (lambda (a) 0))
2 changes: 2 additions & 0 deletions test/test_xxx_lambdaarg_last_comma.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
echo {a,->0}
echo {a, ->0}

0 comments on commit b5f8373

Please sign in to comment.