Skip to content

Commit 50d8ac4

Browse files
authored
Merge pull request #159 from LumaKernel/support-default-args
Support default args
2 parents 463c15b + 5a988fb commit 50d8ac4

File tree

9 files changed

+110
-36
lines changed

9 files changed

+110
-36
lines changed

autoload/vimlparser.vim

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ endfunction
310310
" node rest
311311
" node[] list
312312
" node[] rlist
313+
" node[] default_args
313314
" node[] body
314315
" string op
315316
" string str
@@ -319,7 +320,7 @@ endfunction
319320
" TOPLEVEL .body
320321
" COMMENT .str
321322
" EXCMD .ea .str
322-
" FUNCTION .ea .body .left .rlist .attr .endfunction
323+
" FUNCTION .ea .body .left .rlist .default_args .attr .endfunction
323324
" ENDFUNCTION .ea
324325
" DELFUNCTION .ea .left
325326
" RETURN .ea .left
@@ -1358,6 +1359,7 @@ function! s:VimLParser.parse_cmd_function() abort
13581359
let node.ea = self.ea
13591360
let node.left = left
13601361
let node.rlist = []
1362+
let node.default_args = []
13611363
let node.attr = {'range': 0, 'abort': 0, 'dict': 0, 'closure': 0}
13621364
let node.endfunction = s:NIL
13631365
call self.reader.getn(1)
@@ -1379,6 +1381,12 @@ function! s:VimLParser.parse_cmd_function() abort
13791381
let varnode.pos = token.pos
13801382
let varnode.value = token.value
13811383
call add(node.rlist, varnode)
1384+
if tokenizer.peek().type ==# s:TOKEN_EQ
1385+
call tokenizer.get()
1386+
call add(node.default_args, self.parse_expr())
1387+
elseif len(node.default_args) > 0
1388+
throw s:Err('E989: Non-default argument follows default argument', varnode.pos)
1389+
endif
13821390
" XXX: Vim doesn't skip white space before comma. F(a ,b) => E475
13831391
if s:iswhite(self.reader.p(0)) && tokenizer.peek().type ==# s:TOKEN_COMMA
13841392
throw s:Err('E475: Invalid argument: White space is not allowed before comma', self.reader.getpos())
@@ -4962,14 +4970,25 @@ endfunction
49624970
function! s:Compiler.compile_function(node) abort
49634971
let left = self.compile(a:node.left)
49644972
let rlist = map(a:node.rlist, 'self.compile(v:val)')
4965-
if !empty(rlist) && rlist[-1] ==# '...'
4966-
let rlist[-1] = '. ...'
4967-
endif
4968-
if empty(rlist)
4969-
call self.out('(function (%s)', left)
4970-
else
4971-
call self.out('(function (%s %s)', left, join(rlist, ' '))
4973+
let default_args = map(a:node.default_args, 'self.compile(v:val)')
4974+
if !empty(rlist)
4975+
let remaining = s:FALSE
4976+
if rlist[-1] ==# '...'
4977+
call remove(rlist, -1)
4978+
let remaining = s:TRUE
4979+
endif
4980+
for i in range(len(rlist))
4981+
if i < len(rlist) - len(default_args)
4982+
let left .= printf(' %s', rlist[i])
4983+
else
4984+
let left .= printf(' (%s %s)', rlist[i], default_args[i + len(default_args) - len(rlist)])
4985+
endif
4986+
endfor
4987+
if remaining
4988+
let left .= ' . ...'
4989+
endif
49724990
endif
4991+
call self.out('(function (%s)', left)
49734992
call self.incindent(' ')
49744993
call self.compile_body(a:node.body)
49754994
call self.out(')')

js/vimlparser.js

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@ function ExArg() {
488488
// node rest
489489
// node[] list
490490
// node[] rlist
491+
// node[] default_args
491492
// node[] body
492493
// string op
493494
// string str
@@ -497,7 +498,7 @@ function ExArg() {
497498
// TOPLEVEL .body
498499
// COMMENT .str
499500
// EXCMD .ea .str
500-
// FUNCTION .ea .body .left .rlist .attr .endfunction
501+
// FUNCTION .ea .body .left .rlist .default_args .attr .endfunction
501502
// ENDFUNCTION .ea
502503
// DELFUNCTION .ea .left
503504
// RETURN .ea .left
@@ -1639,6 +1640,7 @@ VimLParser.prototype.parse_cmd_function = function() {
16391640
node.ea = this.ea;
16401641
node.left = left;
16411642
node.rlist = [];
1643+
node.default_args = [];
16421644
node.attr = {"range":0, "abort":0, "dict":0, "closure":0};
16431645
node.endfunction = NIL;
16441646
this.reader.getn(1);
@@ -1662,6 +1664,13 @@ VimLParser.prototype.parse_cmd_function = function() {
16621664
varnode.pos = token.pos;
16631665
varnode.value = token.value;
16641666
viml_add(node.rlist, varnode);
1667+
if (tokenizer.peek().type == TOKEN_EQ) {
1668+
tokenizer.get();
1669+
viml_add(node.default_args, this.parse_expr());
1670+
}
1671+
else if (viml_len(node.default_args) > 0) {
1672+
throw Err("E989: Non-default argument follows default argument", varnode.pos);
1673+
}
16651674
// XXX: Vim doesn't skip white space before comma. F(a ,b) => E475
16661675
if (iswhite(this.reader.p(0)) && tokenizer.peek().type == TOKEN_COMMA) {
16671676
throw Err("E475: Invalid argument: White space is not allowed before comma", this.reader.getpos());
@@ -4465,15 +4474,28 @@ Compiler.prototype.compile_excmd = function(node) {
44654474
Compiler.prototype.compile_function = function(node) {
44664475
var left = this.compile(node.left);
44674476
var rlist = node.rlist.map((function(vval) { return this.compile(vval); }).bind(this));
4468-
if (!viml_empty(rlist) && rlist[rlist.length - 1] == "...") {
4469-
rlist[rlist.length - 1] = ". ...";
4470-
}
4471-
if (viml_empty(rlist)) {
4472-
this.out("(function (%s)", left);
4473-
}
4474-
else {
4475-
this.out("(function (%s %s)", left, viml_join(rlist, " "));
4477+
var default_args = node.default_args.map((function(vval) { return this.compile(vval); }).bind(this));
4478+
if (!viml_empty(rlist)) {
4479+
var remaining = FALSE;
4480+
if (rlist[rlist.length - 1] == "...") {
4481+
viml_remove(rlist, -1);
4482+
var remaining = TRUE;
4483+
}
4484+
var __c11 = viml_range(viml_len(rlist));
4485+
for (var __i11 = 0; __i11 < __c11.length; ++__i11) {
4486+
var i = __c11[__i11];
4487+
if (i < viml_len(rlist) - viml_len(default_args)) {
4488+
left += viml_printf(" %s", rlist[i]);
4489+
}
4490+
else {
4491+
left += viml_printf(" (%s %s)", rlist[i], default_args[i + viml_len(default_args) - viml_len(rlist)]);
4492+
}
4493+
}
4494+
if (remaining) {
4495+
left += " . ...";
4496+
}
44764497
}
4498+
this.out("(function (%s)", left);
44774499
this.incindent(" ");
44784500
this.compile_body(node.body);
44794501
this.out(")");
@@ -4560,9 +4582,9 @@ Compiler.prototype.compile_if = function(node) {
45604582
this.incindent(" ");
45614583
this.compile_body(node.body);
45624584
this.decindent();
4563-
var __c11 = node.elseif;
4564-
for (var __i11 = 0; __i11 < __c11.length; ++__i11) {
4565-
var enode = __c11[__i11];
4585+
var __c12 = node.elseif;
4586+
for (var __i12 = 0; __i12 < __c12.length; ++__i12) {
4587+
var enode = __c12[__i12];
45664588
this.out(" elseif %s", this.compile(enode.cond));
45674589
this.incindent(" ");
45684590
this.compile_body(enode.body);
@@ -4619,9 +4641,9 @@ Compiler.prototype.compile_try = function(node) {
46194641
this.out("(try");
46204642
this.incindent(" ");
46214643
this.compile_body(node.body);
4622-
var __c12 = node.catch;
4623-
for (var __i12 = 0; __i12 < __c12.length; ++__i12) {
4624-
var cnode = __c12[__i12];
4644+
var __c13 = node.catch;
4645+
for (var __i13 = 0; __i13 < __c13.length; ++__i13) {
4646+
var cnode = __c13[__i13];
46254647
if (cnode.pattern !== NIL) {
46264648
this.decindent();
46274649
this.out(" catch /%s/", cnode.pattern);
@@ -5615,9 +5637,9 @@ RegexpParser.prototype.get_token_sq_char_class = function() {
56155637
var r = this.reader.read_alpha();
56165638
if (this.reader.p(0) == ":" && this.reader.p(1) == "]") {
56175639
this.reader.seek_cur(2);
5618-
var __c13 = class_names;
5619-
for (var __i13 = 0; __i13 < __c13.length; ++__i13) {
5620-
var name = __c13[__i13];
5640+
var __c14 = class_names;
5641+
for (var __i14 = 0; __i14 < __c14.length; ++__i14) {
5642+
var name = __c14[__i14];
56215643
if (r == name) {
56225644
return "[:" + name + ":]";
56235645
}
@@ -5750,9 +5772,9 @@ RegexpParser.prototype.getoctchrs = function() {
57505772

57515773
RegexpParser.prototype.gethexchrs = function(n) {
57525774
var r = "";
5753-
var __c14 = viml_range(n);
5754-
for (var __i14 = 0; __i14 < __c14.length; ++__i14) {
5755-
var i = __c14[__i14];
5775+
var __c15 = viml_range(n);
5776+
for (var __i15 = 0; __i15 < __c15.length; ++__i15) {
5777+
var i = __c15[__i15];
57565778
var c = this.reader.peek();
57575779
if (!isxdigit(c)) {
57585780
break;

py/vimlparser.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,7 @@ def ExArg():
476476
# node rest
477477
# node[] list
478478
# node[] rlist
479+
# node[] default_args
479480
# node[] body
480481
# string op
481482
# string str
@@ -485,7 +486,7 @@ def ExArg():
485486
# TOPLEVEL .body
486487
# COMMENT .str
487488
# EXCMD .ea .str
488-
# FUNCTION .ea .body .left .rlist .attr .endfunction
489+
# FUNCTION .ea .body .left .rlist .default_args .attr .endfunction
489490
# ENDFUNCTION .ea
490491
# DELFUNCTION .ea .left
491492
# RETURN .ea .left
@@ -1352,6 +1353,7 @@ def parse_cmd_function(self):
13521353
node.ea = self.ea
13531354
node.left = left
13541355
node.rlist = []
1356+
node.default_args = []
13551357
node.attr = AttributeDict({"range": 0, "abort": 0, "dict": 0, "closure": 0})
13561358
node.endfunction = NIL
13571359
self.reader.getn(1)
@@ -1372,6 +1374,11 @@ def parse_cmd_function(self):
13721374
varnode.pos = token.pos
13731375
varnode.value = token.value
13741376
viml_add(node.rlist, varnode)
1377+
if tokenizer.peek().type == TOKEN_EQ:
1378+
tokenizer.get()
1379+
viml_add(node.default_args, self.parse_expr())
1380+
elif viml_len(node.default_args) > 0:
1381+
raise VimLParserException(Err("E989: Non-default argument follows default argument", varnode.pos))
13751382
# XXX: Vim doesn't skip white space before comma. F(a ,b) => E475
13761383
if iswhite(self.reader.p(0)) and tokenizer.peek().type == TOKEN_COMMA:
13771384
raise VimLParserException(Err("E475: Invalid argument: White space is not allowed before comma", self.reader.getpos()))
@@ -3574,12 +3581,20 @@ def compile_excmd(self, node):
35743581
def compile_function(self, node):
35753582
left = self.compile(node.left)
35763583
rlist = [self.compile(vval) for vval in node.rlist]
3577-
if not viml_empty(rlist) and rlist[-1] == "...":
3578-
rlist[-1] = ". ..."
3579-
if viml_empty(rlist):
3580-
self.out("(function (%s)", left)
3581-
else:
3582-
self.out("(function (%s %s)", left, viml_join(rlist, " "))
3584+
default_args = [self.compile(vval) for vval in node.default_args]
3585+
if not viml_empty(rlist):
3586+
remaining = FALSE
3587+
if rlist[-1] == "...":
3588+
viml_remove(rlist, -1)
3589+
remaining = TRUE
3590+
for i in viml_range(viml_len(rlist)):
3591+
if i < viml_len(rlist) - viml_len(default_args):
3592+
left += viml_printf(" %s", rlist[i])
3593+
else:
3594+
left += viml_printf(" (%s %s)", rlist[i], default_args[i + viml_len(default_args) - viml_len(rlist)])
3595+
if remaining:
3596+
left += " . ..."
3597+
self.out("(function (%s)", left)
35833598
self.incindent(" ")
35843599
self.compile_body(node.body)
35853600
self.out(")")

test/test1.ok

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
; test1
22
(function (s:foo a b . ...)
33
(return 0))
4+
(function (s:bar (a 1) (b 2) . ...)
5+
(return 0))
6+
(function (s:baz a (b 2) . ...)
7+
(return 0))
48
(if 1
59
(echo "if 1")
610
elseif 2

test/test1.vim

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
function s:foo(a, b, ...)
33
return 0
44
endfunction
5+
function s:bar(a = 1, b = 2, ...)
6+
return 0
7+
endfunction
8+
function s:baz(a, b = 2, ...)
9+
return 0
10+
endfunction
511
if 1
612
echo "if 1"
713
elseif 2

test/test_err_funcarg_default.ok

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
vimlparser: E989: Non-default argument follows default argument: line 1 col 21

test/test_err_funcarg_default.vim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
function Foo(abc=1, xyz)
2+
endfunction

test/test_funcarg_default.ok

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
(function (X (foo (+ 1 2)))
2+
(return a:foo))

test/test_funcarg_default.vim

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
function X(foo=1+2)
2+
return a:foo
3+
endfunction

0 commit comments

Comments
 (0)