Creating JVM language [PART 12] - Named Function Arguments

Sources

Why do I need named arguments?

In java (like in most languages) the method call arguments are identified by indexes.
This seems reasonable for a methods with small amount of parameters and preferably different types.
Unfortunately there are many methods that neither have small amount of parameters nor different types.

If you’ve ever done some game programing you proably came across functions like this:

Grammar changes

The function call can have one, or more (splitted by ‘,’ character) arguments.
The rule argument comes in two flavours (unnamed and named).
Mixing named and unnamed arguments is not allowed.

Reordering arguments

As described in Creating JVM language [PART 7] - Methods
, method parsing process is divided into two steps. First it finds all the signatures (declarations), and once it’s done it starts parsing the bodies.
It is guaranteed that during parsing method bodies all the signatures are already available.

Using that characteristics the idea is to “transform” named call to unnamed call by getting parameters indexes from signature:

Look for a parameter name in the signature that matches the argument name

Get parameter index

If the argument is at different index than a parameter reorder it.

In the example above the x2 would be swapped with y1.

publicclassExpressionVisitorextendsEnkelBaseVisitor<Expression>{//other stuff@OverridepublicExpressionvisitFunctionCall(@NotNullEnkelParser.FunctionCallContextctx){StringfunName=ctx.functionName().getText();FunctionSignaturesignature=scope.getSignature(funName);List<EnkelParser.ArgumentContext>argumentsCtx=ctx.argument();//Create comparator that compares arguments based on their index in signatureComparator<EnkelParser.ArgumentContext>argumentComparator=(arg1,arg2)->{if(arg1.name()==null)return0;//If the argument is not named skipStringarg1Name=arg1.name().getText();Stringarg2Name=arg2.name().getText();returnsignature.getIndexOfParameter(arg1Name)-signature.getIndexOfParameter(arg2Name);};List<Expression>arguments=argumentsCtx.stream()//parsed arguments (wrong order).sorted(argumentComparator)//Order using created comparator.map(argument->argument.expression().accept(this))//Map parsed arguments into expressions.collect(toList());returnnewFunctionCall(signature,arguments);}}

That way the component responsible for generting bytecode does not distinct
named and unnamed arguments. It only sees FunctionCall as a collection of arguments (properly ordered) and a signature.
No modifications to bytecode generation are therefore needed.