in internal/compile/compile.go [1667:1733]
func (fcomp *fcomp) args(call *syntax.CallExpr) (op Opcode, arg uint32) {
var callmode int
// Compute the number of each kind of parameter.
var p, n int // number of positional, named arguments
var varargs, kwargs syntax.Expr
for _, arg := range call.Args {
if binary, ok := arg.(*syntax.BinaryExpr); ok && binary.Op == syntax.EQ {
// named argument (name, value)
fcomp.string(binary.X.(*syntax.Ident).Name)
fcomp.expr(binary.Y)
n++
continue
}
if unary, ok := arg.(*syntax.UnaryExpr); ok {
if unary.Op == syntax.STAR {
callmode |= 1
varargs = unary.X
continue
} else if unary.Op == syntax.STARSTAR {
callmode |= 2
kwargs = unary.X
continue
}
}
// positional argument
fcomp.expr(arg)
p++
}
// Python2 and Python3 both permit named arguments
// to appear both before and after a *args argument:
// f(1, 2, x=3, *[4], y=5, **dict(z=6))
//
// They also differ in their evaluation order:
// Python2: 1 2 3 5 4 6 (*args and **kwargs evaluated last)
// Python3: 1 2 4 3 5 6 (positional args evaluated before named args)
// Starlark-in-Java historically used a third order:
// Lexical: 1 2 3 4 5 6 (all args evaluated left-to-right)
//
// After discussion in github.com/bazelbuild/starlark#13, the
// spec now requires Starlark to statically reject named
// arguments after *args (e.g. y=5), and to use Python2-style
// evaluation order. This is both easy to implement and
// consistent with lexical order:
//
// f(1, 2, x=3, *[4], **dict(z=6)) # 1 2 3 4 6
// *args
if varargs != nil {
fcomp.expr(varargs)
}
// **kwargs
if kwargs != nil {
fcomp.expr(kwargs)
}
// TODO(adonovan): avoid this with a more flexible encoding.
if p >= 256 || n >= 256 {
// resolve already checked this; should be unreachable
panic("too many arguments in call")
}
return CALL + Opcode(callmode), uint32(p<<8 | n)
}