Skip to content

Commit 90895c3

Browse files
authored
Merge pull request #163 from vim-jp/fix-method
Fix method operator
2 parents 6356c85 + 103c66e commit 90895c3

File tree

10 files changed

+139
-88
lines changed

10 files changed

+139
-88
lines changed

autoload/vimlparser.vim

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -400,13 +400,14 @@ endfunction
400400
" PLUS .left
401401
" SUBSCRIPT .left .right
402402
" SLICE .left .rlist
403-
" METHOD .left .right .lambda_rlist
403+
" METHOD .left .right
404404
" CALL .left .rlist
405405
" DOT .left .right
406406
" NUMBER .value
407407
" STRING .value
408408
" LIST .value
409409
" DICT .value
410+
" BLOB .value
410411
" NESTING .left
411412
" OPTION .value
412413
" IDENTIFIER .value
@@ -4055,44 +4056,26 @@ function! s:ExprParser.parse_expr8() abort
40554056
endif
40564057
unlet node
40574058
elseif token.type ==# s:TOKEN_ARROW
4059+
let funcname_or_lambda = self.parse_expr9()
4060+
let token = self.tokenizer.get()
4061+
if token.type !=# s:TOKEN_POPEN
4062+
throw s:Err('E107: Missing parentheses: lambda', token.pos)
4063+
endif
4064+
let right = s:Node(s:NODE_CALL)
4065+
let right.pos = token.pos
4066+
let right.left = funcname_or_lambda
4067+
let right.rlist = self.parse_rlist()
40584068
let node = s:Node(s:NODE_METHOD)
40594069
let node.pos = token.pos
40604070
let node.left = left
4061-
let node.right = self.parse_expr8()
4062-
let node.lambda_rlist = s:NIL
4063-
if node.right.type !=# s:NODE_CALL
4064-
throw s:Err('Rhs of method operator must be an function call', node.right.pos)
4065-
endif
4071+
let node.right = right
40664072
let left = node
40674073
unlet node
40684074
elseif token.type ==# s:TOKEN_POPEN
40694075
let node = s:Node(s:NODE_CALL)
40704076
let node.pos = token.pos
40714077
let node.left = left
4072-
let node.rlist = []
4073-
if self.tokenizer.peek().type ==# s:TOKEN_PCLOSE
4074-
call self.tokenizer.get()
4075-
else
4076-
while s:TRUE
4077-
call add(node.rlist, self.parse_expr1())
4078-
let token = self.tokenizer.get()
4079-
if token.type ==# s:TOKEN_COMMA
4080-
" XXX: Vim allows foo(a, b, ). Lint should warn it.
4081-
if self.tokenizer.peek().type ==# s:TOKEN_PCLOSE
4082-
call self.tokenizer.get()
4083-
break
4084-
endif
4085-
elseif token.type ==# s:TOKEN_PCLOSE
4086-
break
4087-
else
4088-
throw s:Err(printf('unexpected token: %s', token.value), token.pos)
4089-
endif
4090-
endwhile
4091-
endif
4092-
if len(node.rlist) > s:MAX_FUNC_ARGS
4093-
" TODO: funcname E740: Too many arguments for function: %s
4094-
throw s:Err('E740: Too many arguments for function', node.pos)
4095-
endif
4078+
let node.rlist = self.parse_rlist()
40964079
let left = node
40974080
unlet node
40984081
elseif !s:iswhite(c) && token.type ==# s:TOKEN_DOT " TODO check scriptversion?
@@ -4111,6 +4094,35 @@ function! s:ExprParser.parse_expr8() abort
41114094
return left
41124095
endfunction
41134096

4097+
function! s:ExprParser.parse_rlist() abort
4098+
let rlist = []
4099+
let token = self.tokenizer.peek()
4100+
if self.tokenizer.peek().type ==# s:TOKEN_PCLOSE
4101+
call self.tokenizer.get()
4102+
else
4103+
while s:TRUE
4104+
call add(rlist, self.parse_expr1())
4105+
let token = self.tokenizer.get()
4106+
if token.type ==# s:TOKEN_COMMA
4107+
" XXX: Vim allows foo(a, b, ). Lint should warn it.
4108+
if self.tokenizer.peek().type ==# s:TOKEN_PCLOSE
4109+
call self.tokenizer.get()
4110+
break
4111+
endif
4112+
elseif token.type ==# s:TOKEN_PCLOSE
4113+
break
4114+
else
4115+
throw s:Err(printf('unexpected token: %s', token.value), token.pos)
4116+
endif
4117+
endwhile
4118+
endif
4119+
if len(rlist) > s:MAX_FUNC_ARGS
4120+
" TODO: funcname E740: Too many arguments for function: %s
4121+
throw s:Err('E740: Too many arguments for function', token.pos)
4122+
endif
4123+
return rlist
4124+
endfunction
4125+
41144126
" expr9: number
41154127
" "string"
41164128
" 'string'

js/vimlparser.js

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -578,13 +578,14 @@ function ExArg() {
578578
// PLUS .left
579579
// SUBSCRIPT .left .right
580580
// SLICE .left .rlist
581-
// METHOD .left .right .lambda_rlist
581+
// METHOD .left .right
582582
// CALL .left .rlist
583583
// DOT .left .right
584584
// NUMBER .value
585585
// STRING .value
586586
// LIST .value
587587
// DICT .value
588+
// BLOB .value
588589
// NESTING .left
589590
// OPTION .value
590591
// IDENTIFIER .value
@@ -3399,48 +3400,27 @@ ExprParser.prototype.parse_expr8 = function() {
33993400
delete node;
34003401
}
34013402
else if (token.type == TOKEN_ARROW) {
3403+
var funcname_or_lambda = this.parse_expr9();
3404+
var token = this.tokenizer.get();
3405+
if (token.type != TOKEN_POPEN) {
3406+
throw Err("E107: Missing parentheses: lambda", token.pos);
3407+
}
3408+
var right = Node(NODE_CALL);
3409+
right.pos = token.pos;
3410+
right.left = funcname_or_lambda;
3411+
right.rlist = this.parse_rlist();
34023412
var node = Node(NODE_METHOD);
34033413
node.pos = token.pos;
34043414
node.left = left;
3405-
node.right = this.parse_expr8();
3406-
node.lambda_rlist = NIL;
3407-
if (node.right.type != NODE_CALL) {
3408-
throw Err("Rhs of method operator must be an function call", node.right.pos);
3409-
}
3415+
node.right = right;
34103416
var left = node;
34113417
delete node;
34123418
}
34133419
else if (token.type == TOKEN_POPEN) {
34143420
var node = Node(NODE_CALL);
34153421
node.pos = token.pos;
34163422
node.left = left;
3417-
node.rlist = [];
3418-
if (this.tokenizer.peek().type == TOKEN_PCLOSE) {
3419-
this.tokenizer.get();
3420-
}
3421-
else {
3422-
while (TRUE) {
3423-
viml_add(node.rlist, this.parse_expr1());
3424-
var token = this.tokenizer.get();
3425-
if (token.type == TOKEN_COMMA) {
3426-
// XXX: Vim allows foo(a, b, ). Lint should warn it.
3427-
if (this.tokenizer.peek().type == TOKEN_PCLOSE) {
3428-
this.tokenizer.get();
3429-
break;
3430-
}
3431-
}
3432-
else if (token.type == TOKEN_PCLOSE) {
3433-
break;
3434-
}
3435-
else {
3436-
throw Err(viml_printf("unexpected token: %s", token.value), token.pos);
3437-
}
3438-
}
3439-
}
3440-
if (viml_len(node.rlist) > MAX_FUNC_ARGS) {
3441-
// TODO: funcname E740: Too many arguments for function: %s
3442-
throw Err("E740: Too many arguments for function", node.pos);
3443-
}
3423+
node.rlist = this.parse_rlist();
34443424
var left = node;
34453425
delete node;
34463426
}
@@ -3462,6 +3442,38 @@ ExprParser.prototype.parse_expr8 = function() {
34623442
return left;
34633443
}
34643444

3445+
ExprParser.prototype.parse_rlist = function() {
3446+
var rlist = [];
3447+
var token = this.tokenizer.peek();
3448+
if (this.tokenizer.peek().type == TOKEN_PCLOSE) {
3449+
this.tokenizer.get();
3450+
}
3451+
else {
3452+
while (TRUE) {
3453+
viml_add(rlist, this.parse_expr1());
3454+
var token = this.tokenizer.get();
3455+
if (token.type == TOKEN_COMMA) {
3456+
// XXX: Vim allows foo(a, b, ). Lint should warn it.
3457+
if (this.tokenizer.peek().type == TOKEN_PCLOSE) {
3458+
this.tokenizer.get();
3459+
break;
3460+
}
3461+
}
3462+
else if (token.type == TOKEN_PCLOSE) {
3463+
break;
3464+
}
3465+
else {
3466+
throw Err(viml_printf("unexpected token: %s", token.value), token.pos);
3467+
}
3468+
}
3469+
}
3470+
if (viml_len(rlist) > MAX_FUNC_ARGS) {
3471+
// TODO: funcname E740: Too many arguments for function: %s
3472+
throw Err("E740: Too many arguments for function", token.pos);
3473+
}
3474+
return rlist;
3475+
}
3476+
34653477
// expr9: number
34663478
// "string"
34673479
// 'string'

py/vimlparser.py

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -567,13 +567,14 @@ def ExArg():
567567
# PLUS .left
568568
# SUBSCRIPT .left .right
569569
# SLICE .left .rlist
570-
# METHOD .left .right .lambda_rlist
570+
# METHOD .left .right
571571
# CALL .left .rlist
572572
# DOT .left .right
573573
# NUMBER .value
574574
# STRING .value
575575
# LIST .value
576576
# DICT .value
577+
# BLOB .value
577578
# NESTING .left
578579
# OPTION .value
579580
# IDENTIFIER .value
@@ -2771,38 +2772,25 @@ def parse_expr8(self):
27712772
left = node
27722773
del node
27732774
elif token.type == TOKEN_ARROW:
2775+
funcname_or_lambda = self.parse_expr9()
2776+
token = self.tokenizer.get()
2777+
if token.type != TOKEN_POPEN:
2778+
raise VimLParserException(Err("E107: Missing parentheses: lambda", token.pos))
2779+
right = Node(NODE_CALL)
2780+
right.pos = token.pos
2781+
right.left = funcname_or_lambda
2782+
right.rlist = self.parse_rlist()
27742783
node = Node(NODE_METHOD)
27752784
node.pos = token.pos
27762785
node.left = left
2777-
node.right = self.parse_expr8()
2778-
node.lambda_rlist = NIL
2779-
if node.right.type != NODE_CALL:
2780-
raise VimLParserException(Err("Rhs of method operator must be an function call", node.right.pos))
2786+
node.right = right
27812787
left = node
27822788
del node
27832789
elif token.type == TOKEN_POPEN:
27842790
node = Node(NODE_CALL)
27852791
node.pos = token.pos
27862792
node.left = left
2787-
node.rlist = []
2788-
if self.tokenizer.peek().type == TOKEN_PCLOSE:
2789-
self.tokenizer.get()
2790-
else:
2791-
while TRUE:
2792-
viml_add(node.rlist, self.parse_expr1())
2793-
token = self.tokenizer.get()
2794-
if token.type == TOKEN_COMMA:
2795-
# XXX: Vim allows foo(a, b, ). Lint should warn it.
2796-
if self.tokenizer.peek().type == TOKEN_PCLOSE:
2797-
self.tokenizer.get()
2798-
break
2799-
elif token.type == TOKEN_PCLOSE:
2800-
break
2801-
else:
2802-
raise VimLParserException(Err(viml_printf("unexpected token: %s", token.value), token.pos))
2803-
if viml_len(node.rlist) > MAX_FUNC_ARGS:
2804-
# TODO: funcname E740: Too many arguments for function: %s
2805-
raise VimLParserException(Err("E740: Too many arguments for function", node.pos))
2793+
node.rlist = self.parse_rlist()
28062794
left = node
28072795
del node
28082796
elif not iswhite(c) and token.type == TOKEN_DOT:
@@ -2818,6 +2806,29 @@ def parse_expr8(self):
28182806
break
28192807
return left
28202808

2809+
def parse_rlist(self):
2810+
rlist = []
2811+
token = self.tokenizer.peek()
2812+
if self.tokenizer.peek().type == TOKEN_PCLOSE:
2813+
self.tokenizer.get()
2814+
else:
2815+
while TRUE:
2816+
viml_add(rlist, self.parse_expr1())
2817+
token = self.tokenizer.get()
2818+
if token.type == TOKEN_COMMA:
2819+
# XXX: Vim allows foo(a, b, ). Lint should warn it.
2820+
if self.tokenizer.peek().type == TOKEN_PCLOSE:
2821+
self.tokenizer.get()
2822+
break
2823+
elif token.type == TOKEN_PCLOSE:
2824+
break
2825+
else:
2826+
raise VimLParserException(Err(viml_printf("unexpected token: %s", token.value), token.pos))
2827+
if viml_len(rlist) > MAX_FUNC_ARGS:
2828+
# TODO: funcname E740: Too many arguments for function: %s
2829+
raise VimLParserException(Err("E740: Too many arguments for function", token.pos))
2830+
return rlist
2831+
28212832
# expr9: number
28222833
# "string"
28232834
# 'string'

test/test1.ok

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
(let %= a 5)
6060
(let ..= a 'foo')
6161
(echo (concat (concat 'foo' 'bar') 'baz'))
62+
(echo (concat 'foo' (concat 'bar' 'baz')))
63+
(echo (concat (concat 'foo' 'bar') 'baz'))
6264
(let = a '🐥')
6365
(const = a 1)
6466
(const = (a b) (list 1 2))

test/test1.vim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ let a /= 4
6666
let a %= 5
6767
let a ..= 'foo'
6868
echo ('foo' .. 'bar')..'baz'
69+
echo 'foo' .. ('bar'..'baz')
70+
echo 'foo' .. 'bar' .. 'baz'
6971
let a = '🐥'
7072
const a = 1
7173
const [a, b] = [1, 2]

test/test_err_method.ok

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
vimlparser: E107: Missing parentheses: lambda: line 1 col 32

test/test_err_method.vim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
echo 'foo'->{s -> s .. ' bar' }

test/test_err_toomanyarg.ok

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
vimlparser: E740: Too many arguments for function: line 1 col 9
1+
vimlparser: E740: Too many arguments for function: line 1 col 83

test/test_method.ok

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,8 @@
44
(echo (method 'john' ((lambda (s) (concat 'hello ' s)))))
55
(echo (method 'john' ((lambda (s b) (concat (concat (concat (concat 'hello ' s) ' ... and goodbye ') b) '!')) 'bob')))
66
(echo (method 'john' ((lambda (...) (concat 'hello ' (method a:000 (join ', ')))) 'bob')))
7+
(eval (method funcs (map (lambda () (v:val)))))
8+
(let = session (method (method (s:get_sessions) (filter (lambda () (==# (dot v:val name) session_name)))) (get 0 (dict))))
9+
(let = session (method (method (s:get_sessions) (filter (lambda () (==# (dot v:val name) session_name)))) (get 0 (dict))))
10+
(echo (method expr8 (s:flatmap)))
11+
(echo (method (method expr8 (s:flatmap)) (filter (lambda (i) (% i 2)))))

test/test_method.vim

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,8 @@ echo [1,2,3]->map({i,n -> n * 2})
44
echo 'john'->{s -> 'hello ' .. s }()
55
echo 'john'->{s,b -> 'hello ' .. s .. ' ... and goodbye ' .. b .. '!' }('bob')
66
echo 'john'->{... -> 'hello ' .. a:000->join(', ') }('bob')
7+
eval funcs->map({-> v:val() })
8+
let session = s:get_sessions()->filter({-> v:val.name ==# session_name })->get(0, {})
9+
let session = (s:get_sessions()->filter({-> v:val.name ==# session_name }))->get(0, {})
10+
echo expr8->s:flatmap()
11+
echo expr8->s:flatmap()->filter({i -> i % 2})

0 commit comments

Comments
 (0)