Welcome to the Tsonnet series!
If you're not following along, check out how it all started in the first post of the series.
In the previous post, we wrapped up function support with named parameters at call sites:
Oops, I forgot function calls without arguments.
Classic. I left them home alone.
What must be validated
Here's what we need to handle:
local greet() = 'hello';
local make_obj() = { x: 1, y: 2 };
local apply(f) = f();
local obj = { m(): 7 };
{
greet: greet(),
obj: make_obj(),
inline: (function() 42)(),
applied: apply(function() 'world'),
method_call: obj.m(),
}
Parsing
The fix is very straightforward. The only change required is substituting separated_nonempty_list by separated_list:
diff --git a/lib/parser.mly b/lib/parser.mly
index 94f1a2f..781ec5d 100644
--- a/lib/parser.mly
+++ b/lib/parser.mly
@@ -102,7 +102,7 @@ obj_key:
obj_field:
| k = obj_key; COLON; e = assignable_expr { ObjectField (k, e) }
| k = obj_key;
- LEFT_PAREN; params = separated_nonempty_list(COMMA, fundef_param); RIGHT_PAREN;
+ LEFT_PAREN; params = separated_list(COMMA, fundef_param); RIGHT_PAREN;
COLON; body = assignable_expr
{ ObjectField (
k,
@@ -201,7 +201,7 @@ fundef_param:
fundef:
| fname = ID;
- LEFT_PAREN; params = separated_nonempty_list(COMMA, fundef_param); RIGHT_PAREN;
+ LEFT_PAREN; params = separated_list(COMMA, fundef_param); RIGHT_PAREN;
ASSIGN;
body = fundef_body { { name = fname; params = params; body = body } }
;
@@ -218,7 +218,7 @@ funcall_arg:
funcall:
| fname = ID;
- LEFT_PAREN; params = separated_nonempty_list(COMMA, funcall_arg); RIGHT_PAREN
+ LEFT_PAREN; params = separated_list(COMMA, funcall_arg); RIGHT_PAREN
{ FunctionCall
(with_pos $startpos $endpos, {
callee = Ident (with_pos $startpos(fname) $endpos(fname), fname);
@@ -226,16 +226,16 @@ funcall:
})
}
| callee = scoped_expr;
- LEFT_PAREN; params = separated_nonempty_list(COMMA, funcall_arg); RIGHT_PAREN
+ LEFT_PAREN; params = separated_list(COMMA, funcall_arg); RIGHT_PAREN
{ FunctionCall (with_pos $startpos $endpos, { callee = callee; args = params }) }
| callee = obj_field_access;
- LEFT_PAREN; params = separated_nonempty_list(COMMA, funcall_arg); RIGHT_PAREN
+ LEFT_PAREN; params = separated_list(COMMA, funcall_arg); RIGHT_PAREN
{ FunctionCall (with_pos $startpos $endpos, { callee = callee; args = params }) }
;
closure:
| FUNCTION;
- LEFT_PAREN; params = separated_nonempty_list(COMMA, fundef_param); RIGHT_PAREN;
+ LEFT_PAREN; params = separated_list(COMMA, fundef_param); RIGHT_PAREN;
body = assignable_expr {
Closure (with_pos $startpos $endpos, { params = params; body = body })
}
Every place we parse parameter or argument lists gets the same treatment: function definitions, object method definitions, function calls, and closures. Swap nonempty out, and zero-argument calls stop crashing the parser.
How could I forget this one? XD
Testing
A new sample and a cram test entry:
diff --git a/test/cram/functions.t b/test/cram/functions.t
index 89cfd27..44f7d44 100644
--- a/test/cram/functions.t
+++ b/test/cram/functions.t
@@ -15,3 +15,12 @@
$ tsonnet ../../samples/functions/method.jsonnet
4
+
+ $ tsonnet ../../samples/functions/no_args.jsonnet
+ {
+ "applied": "world",
+ "greet": "hello",
+ "inline": 42,
+ "method_call": 7,
+ "obj": { "x": 1, "y": 2 }
+ }
Zero arguments, zero drama.
Conclusion
Short one, but a necessary one. No-argument function calls were quietly broken, and the fix turned out to be a one-word change per grammar rule. Easy to overlook, easy to fix once you notice.
Next up: conditionals. The Jsonnet tutorial can't wait forever.
The entire diff can be seen here.
Thanks for reading Bit Maybe Wise! Left no-arg calls home alone. Subscribe so you don't miss when conditionals finally move in.

Top comments (0)