From 52153852d546514d9bf61e444fe5d829f3835476 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Wed, 13 Feb 2019 19:18:15 -0500 Subject: Support keyword-only function parameters (#143) Following a hitherto undocumented feature of Skylark-in-Java, which in turn follows Python3, a function declaration may now include optional parameters after the *args parameter: ``` def f(a, b, c=1, *args, d=2, **kwargs) ``` The parameter d is a "keyword-only" parameter as it can never by assigned from a positional parameter; all positional arguments surplus to a, b, and c are put in a tuple and assigned to args. To declare a non-variadic function with keyword-only arguments, the *args parameter is replaced by just *: ``` def f(a, b, c=1, *, d=2, **kwargs) ``` The * parameter is not a real parameter; it just serves as a separator between the parameter that may be specified positionally and the keyword-only ones. Spec proposal at bazelbuild/starlark#23 Fixes #61 --- internal/compile/compile.go | 14 +++++++++++--- internal/compile/serial.go | 26 +++++++++++++++----------- 2 files changed, 26 insertions(+), 14 deletions(-) (limited to 'internal') diff --git a/internal/compile/compile.go b/internal/compile/compile.go index 6b43fbe..b313a5c 100644 --- a/internal/compile/compile.go +++ b/internal/compile/compile.go @@ -37,7 +37,7 @@ import ( const debug = false // TODO(adonovan): use a bitmap of options; and regexp to match files // Increment this to force recompilation of saved bytecode files. -const Version = 5 +const Version = 6 type Opcode uint8 @@ -305,10 +305,11 @@ type Funcode struct { Doc string // docstring of this function Code []byte // the byte code pclinetab []uint16 // mapping from pc to linenum - Locals []Ident // for error messages and tracing + Locals []Ident // locals, parameters first Freevars []Ident // for tracing MaxStack int NumParams int + NumKwonlyParams int HasVarargs, HasKwargs bool } @@ -1705,7 +1706,14 @@ func (fcomp *fcomp) function(pos syntax.Position, name string, f *syntax.Functio fmt.Fprintf(os.Stderr, "resuming %s @ %s\n", fcomp.fn.Name, fcomp.pos) } - funcode.NumParams = len(f.Params) + // def f(a, *, b=1) has only 2 parameters. + numParams := len(f.Params) + if f.NumKwonlyParams > 0 && !f.HasVarargs { + numParams-- + } + + funcode.NumParams = numParams + funcode.NumKwonlyParams = f.NumKwonlyParams funcode.HasVarargs = f.HasVarargs funcode.HasKwargs = f.HasKwargs fcomp.emit1(MAKEFUNC, fcomp.pcomp.functionIndex(funcode)) diff --git a/internal/compile/serial.go b/internal/compile/serial.go index f6cf807..6056ea7 100644 --- a/internal/compile/serial.go +++ b/internal/compile/serial.go @@ -39,6 +39,7 @@ package compile // freevar []Ident // maxstack varint // numparams varint +// numkwonlyparams varint // hasvarargs varint (0 or 1) // haskwargs varint (0 or 1) // @@ -185,6 +186,7 @@ func (e *encoder) function(fn *Funcode) { e.idents(fn.Freevars) e.int(fn.MaxStack) e.int(fn.NumParams) + e.int(fn.NumKwonlyParams) e.int(b2i(fn.HasVarargs)) e.int(b2i(fn.HasKwargs)) } @@ -350,20 +352,22 @@ func (d *decoder) function() *Funcode { freevars := d.idents() maxStack := d.int() numParams := d.int() + numKwonlyParams := d.int() hasVarargs := d.int() != 0 hasKwargs := d.int() != 0 return &Funcode{ // Prog is filled in later. - Pos: id.Pos, - Name: id.Name, - Doc: doc, - Code: code, - pclinetab: pclinetab, - Locals: locals, - Freevars: freevars, - MaxStack: maxStack, - NumParams: numParams, - HasVarargs: hasVarargs, - HasKwargs: hasKwargs, + Pos: id.Pos, + Name: id.Name, + Doc: doc, + Code: code, + pclinetab: pclinetab, + Locals: locals, + Freevars: freevars, + MaxStack: maxStack, + NumParams: numParams, + NumKwonlyParams: numKwonlyParams, + HasVarargs: hasVarargs, + HasKwargs: hasKwargs, } } -- cgit v1.2.3