Commit b29461f0 authored by Dylan Trotter's avatar Dylan Trotter

Implement code object.

parent c6065be4
...@@ -405,18 +405,19 @@ class ExprVisitor(ast.NodeVisitor): ...@@ -405,18 +405,19 @@ class ExprVisitor(ast.NodeVisitor):
tmpl = '$args[$i] = πg.FunctionArg{Name: $name, Def: $default}' tmpl = '$args[$i] = πg.FunctionArg{Name: $name, Def: $default}'
self.writer.write_tmpl(tmpl, args=func_args.expr, i=i, self.writer.write_tmpl(tmpl, args=func_args.expr, i=i,
name=util.go_str(a.id), default=default.expr) name=util.go_str(a.id), default=default.expr)
flags = []
vararg = args.vararg if args.vararg else '' if args.vararg:
kwarg = args.kwarg if args.kwarg else '' flags.append('πg.CodeFlagVarArg')
if args.kwarg:
flags.append('πg.CodeFlagKWArg')
# The function object gets written to a temporary writer because we need # The function object gets written to a temporary writer because we need
# it as an expression that we subsequently bind to some variable. # it as an expression that we subsequently bind to some variable.
self.writer.write_tmpl( self.writer.write_tmpl(
'$result = πg.NewFunction(' '$result = πg.NewFunction('
'$name, $args, $vararg, $kwarg, func(' '$name, πg.NewCode($name, $args, $flags, func('
'πF *πg.Frame, πArgs []*πg.Object) (*πg.Object, *πg.BaseException) {', 'πF *πg.Frame, πArgs []*πg.Object) (*πg.Object, *πg.BaseException) {',
result=result.name, name=util.go_str(node.name), args=func_args.expr, result=result.name, name=util.go_str(node.name), args=func_args.expr,
vararg=util.go_str(vararg), kwarg=util.go_str(kwarg)) flags=' | '.join(flags) if flags else 0)
with self.writer.indent_block(): with self.writer.indent_block():
# Declare the local variables used in this function. # Declare the local variables used in this function.
for var in func_block.vars.values(): for var in func_block.vars.values():
...@@ -440,7 +441,7 @@ class ExprVisitor(ast.NodeVisitor): ...@@ -440,7 +441,7 @@ class ExprVisitor(ast.NodeVisitor):
with self.writer.indent_block(-1): with self.writer.indent_block(-1):
self.writer.write(body) self.writer.write(body)
self.writer.write('return πg.None, nil') self.writer.write('return πg.None, nil')
self.writer.write('}).ToObject()') self.writer.write('})).ToObject()')
return result return result
def _visit_seq_elts(self, elts): def _visit_seq_elts(self, elts):
......
...@@ -14,21 +14,13 @@ ...@@ -14,21 +14,13 @@
package grumpy package grumpy
import (
"reflect"
)
// FrameType is the object representing the Python 'frame' type.
var BlockType = newBasisType("code", reflect.TypeOf(Block{}), toBlockUnsafe, ObjectType)
// Block is a handle to code that runs in a new scope such as a function, class // Block is a handle to code that runs in a new scope such as a function, class
// or module. // or module.
type Block struct { type Block struct {
Object
// name is the name of the compiled function or class, or "<module>". // name is the name of the compiled function or class, or "<module>".
name string `attr:"co_name"` name string
// filename is the path of the file where the Python code originated. // filename is the path of the file where the Python code originated.
filename string `attr:"co_filename"` filename string
// fn is a closure that executes the body of the code block. It may be // fn is a closure that executes the body of the code block. It may be
// re-entered multiple times, e.g. for exception handling. // re-entered multiple times, e.g. for exception handling.
fn func(*Frame, *Object) (*Object, *BaseException) fn func(*Frame, *Object) (*Object, *BaseException)
...@@ -36,11 +28,7 @@ type Block struct { ...@@ -36,11 +28,7 @@ type Block struct {
// NewBlock creates a Block object. // NewBlock creates a Block object.
func NewBlock(name, filename string, fn func(*Frame, *Object) (*Object, *BaseException)) *Block { func NewBlock(name, filename string, fn func(*Frame, *Object) (*Object, *BaseException)) *Block {
return &Block{Object{typ: BlockType}, name, filename, fn} return &Block{name, filename, fn}
}
func toBlockUnsafe(o *Object) *Block {
return (*Block)(o.toPointer())
} }
// Exec runs b in the context of a new child frame of back. // Exec runs b in the context of a new child frame of back.
...@@ -80,7 +68,3 @@ func (b *Block) execInternal(f *Frame, sendValue *Object) (*Object, *BaseExcepti ...@@ -80,7 +68,3 @@ func (b *Block) execInternal(f *Frame, sendValue *Object) (*Object, *BaseExcepti
f.state = f.PopCheckpoint() f.state = f.PopCheckpoint()
} }
} }
func initBlockType(dict map[string]*Object) {
BlockType.flags &= ^(typeFlagInstantiable | typeFlagBasetype)
}
...@@ -82,9 +82,9 @@ var builtinTypes = map[*Type]*builtinTypeInfo{ ...@@ -82,9 +82,9 @@ var builtinTypes = map[*Type]*builtinTypeInfo{
AttributeErrorType: {global: true}, AttributeErrorType: {global: true},
BaseExceptionType: {init: initBaseExceptionType, global: true}, BaseExceptionType: {init: initBaseExceptionType, global: true},
BaseStringType: {init: initBaseStringType, global: true}, BaseStringType: {init: initBaseStringType, global: true},
BlockType: {init: initBlockType},
BoolType: {init: initBoolType, global: true}, BoolType: {init: initBoolType, global: true},
BytesWarningType: {global: true}, BytesWarningType: {global: true},
CodeType: {},
DeprecationWarningType: {global: true}, DeprecationWarningType: {global: true},
dictItemIteratorType: {init: initDictItemIteratorType}, dictItemIteratorType: {init: initDictItemIteratorType},
dictKeyIteratorType: {init: initDictKeyIteratorType}, dictKeyIteratorType: {init: initDictKeyIteratorType},
......
package grumpy
import (
"fmt"
"reflect"
)
// CodeType is the object representing the Python 'code' type.
var CodeType = newBasisType("code", reflect.TypeOf(Code{}), toCodeUnsafe, ObjectType)
type CodeFlag int
const (
CodeFlagVarArg CodeFlag = 4
CodeFlagKWArg CodeFlag = 8
)
type Code struct {
Object
name string
// argc is the number of positional arguments.
argc int `attr:"co_argcount"`
// minArgc is the number of positional non-keyword arguments (i.e. the
// minimum number of positional arguments that must be passed).
minArgc int
flags CodeFlag `attr:"co_flags"`
args []FunctionArg
fn func(*Frame, []*Object) (*Object, *BaseException)
}
func NewCode(name string, args []FunctionArg, flags CodeFlag, fn func(*Frame, []*Object) (*Object, *BaseException)) *Code {
argc := len(args)
minArgc := 0
for ; minArgc < argc; minArgc++ {
if args[minArgc].Def != nil {
break
}
}
for _, arg := range args[minArgc:argc] {
if arg.Def == nil {
format := "%s() non-keyword arg %s after keyword arg"
logFatal(fmt.Sprintf(format, name, arg.Name))
}
}
return &Code{Object{typ: CodeType}, name, argc, minArgc, flags, args, fn}
}
func toCodeUnsafe(o *Object) *Code {
return (*Code)(o.toPointer())
}
func (c *Code) call(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
argc := len(args)
if argc > c.argc && c.flags&CodeFlagVarArg == 0 {
format := "%s() takes %d arguments (%d given)"
return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, c.name, c.argc, argc))
}
numBodyArgs := c.argc
varArgIndex, kwArgIndex := -1, -1
if c.flags&CodeFlagVarArg != 0 {
varArgIndex = numBodyArgs
numBodyArgs++
}
if c.flags&CodeFlagKWArg != 0 {
kwArgIndex = numBodyArgs
numBodyArgs++
}
bodyArgs := f.MakeArgs(numBodyArgs)
i := 0
for ; i < argc && i < c.argc; i++ {
bodyArgs[i] = args[i]
}
if varArgIndex != -1 {
bodyArgs[varArgIndex] = NewTuple(args[i:].makeCopy()...).ToObject()
}
var kwargDict *Dict
if kwArgIndex != -1 {
kwargDict = NewDict()
bodyArgs[kwArgIndex] = kwargDict.ToObject()
}
for _, kw := range kwargs {
name := kw.Name
j := 0
for ; j < c.argc; j++ {
if c.args[j].Name == name {
if bodyArgs[j] != nil {
format := "%s() got multiple values for keyword argument '%s'"
return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, c.name, name))
}
bodyArgs[j] = kw.Value
break
}
}
if j == c.argc {
if kwargDict == nil {
format := "%s() got an unexpected keyword argument '%s'"
return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, c.name, name))
}
if raised := kwargDict.SetItemString(f, name, kw.Value); raised != nil {
return nil, raised
}
}
}
for ; i < c.argc; i++ {
arg := c.args[i]
if bodyArgs[i] == nil {
if arg.Def == nil {
format := "%s() takes at least %d arguments (%d given)"
return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, c.name, c.minArgc, argc))
}
bodyArgs[i] = arg.Def
}
}
ret, raised := c.fn(f, bodyArgs)
f.FreeArgs(bodyArgs)
return ret, raised
}
package grumpy
import (
"testing"
)
func TestXxx(t *testing.T) {
}
func TestNewCodeKeywordsCheck(t *testing.T) {
oldLogFatal := logFatal
defer func() { logFatal = oldLogFatal }()
var got string
logFatal = func(msg string) {
got = msg
}
NewCode("foo", []FunctionArg{{"bar", None}, {"baz", nil}}, 0, nil)
if want := "foo() non-keyword arg baz after keyword arg"; got != want {
t.Errorf("NewCode logged %q, want %q", got, want)
}
}
func TestNewCode(t *testing.T) {
testFunc := newBuiltinFunction("TestNewCode", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
if raised := checkFunctionVarArgs(f, "TestNewCode", args, CodeType); raised != nil {
return nil, raised
}
return toCodeUnsafe(args[0]).call(f, args[1:], kwargs)
})
fn := func(f *Frame, args []*Object) (*Object, *BaseException) {
return NewTuple(Args(args).makeCopy()...).ToObject(), nil
}
cases := []invokeTestCase{
invokeTestCase{args: wrapArgs(NewCode("f1", nil, 0, fn)), want: NewTuple().ToObject()},
invokeTestCase{args: wrapArgs(NewCode("f2", []FunctionArg{{"a", nil}}, 0, fn), 123), want: newTestTuple(123).ToObject()},
invokeTestCase{args: wrapArgs(NewCode("f2", []FunctionArg{{"a", nil}}, 0, fn)), kwargs: wrapKWArgs("a", "apple"), want: newTestTuple("apple").ToObject()},
invokeTestCase{args: wrapArgs(NewCode("f2", []FunctionArg{{"a", nil}}, 0, fn)), kwargs: wrapKWArgs("b", "bear"), wantExc: mustCreateException(TypeErrorType, "f2() got an unexpected keyword argument 'b'")},
invokeTestCase{args: wrapArgs(NewCode("f2", []FunctionArg{{"a", nil}}, 0, fn)), wantExc: mustCreateException(TypeErrorType, "f2() takes at least 1 arguments (0 given)")},
invokeTestCase{args: wrapArgs(NewCode("f2", []FunctionArg{{"a", nil}}, 0, fn), 1, 2, 3), wantExc: mustCreateException(TypeErrorType, "f2() takes 1 arguments (3 given)")},
invokeTestCase{args: wrapArgs(NewCode("f3", []FunctionArg{{"a", nil}, {"b", nil}}, 0, fn), 1, 2), want: newTestTuple(1, 2).ToObject()},
invokeTestCase{args: wrapArgs(NewCode("f3", []FunctionArg{{"a", nil}, {"b", nil}}, 0, fn), 1), kwargs: wrapKWArgs("b", "bear"), want: newTestTuple(1, "bear").ToObject()},
invokeTestCase{args: wrapArgs(NewCode("f3", []FunctionArg{{"a", nil}, {"b", nil}}, 0, fn)), kwargs: wrapKWArgs("b", "bear", "a", "apple"), want: newTestTuple("apple", "bear").ToObject()},
invokeTestCase{args: wrapArgs(NewCode("f3", []FunctionArg{{"a", nil}, {"b", nil}}, 0, fn), 1), kwargs: wrapKWArgs("a", "alpha"), wantExc: mustCreateException(TypeErrorType, "f3() got multiple values for keyword argument 'a'")},
invokeTestCase{args: wrapArgs(NewCode("f4", []FunctionArg{{"a", nil}, {"b", None}}, 0, fn), 123), want: newTestTuple(123, None).ToObject()},
invokeTestCase{args: wrapArgs(NewCode("f4", []FunctionArg{{"a", nil}, {"b", None}}, 0, fn), 123, "bar"), want: newTestTuple(123, "bar").ToObject()},
invokeTestCase{args: wrapArgs(NewCode("f4", []FunctionArg{{"a", nil}, {"b", None}}, 0, fn)), kwargs: wrapKWArgs("a", 123, "b", "bar"), want: newTestTuple(123, "bar").ToObject()},
invokeTestCase{args: wrapArgs(NewCode("f5", []FunctionArg{{"a", nil}}, CodeFlagVarArg, fn), 1), want: newTestTuple(1, NewTuple()).ToObject()},
invokeTestCase{args: wrapArgs(NewCode("f5", []FunctionArg{{"a", nil}}, CodeFlagVarArg, fn), 1, 2, 3), want: newTestTuple(1, newTestTuple(2, 3)).ToObject()},
invokeTestCase{args: wrapArgs(NewCode("f6", []FunctionArg{{"a", nil}}, CodeFlagKWArg, fn), "bar"), want: newTestTuple("bar", NewDict()).ToObject()},
invokeTestCase{args: wrapArgs(NewCode("f6", []FunctionArg{{"a", nil}}, CodeFlagKWArg, fn)), kwargs: wrapKWArgs("a", "apple", "b", "bear"), want: newTestTuple("apple", newTestDict("b", "bear")).ToObject()},
invokeTestCase{args: wrapArgs(NewCode("f6", []FunctionArg{{"a", nil}}, CodeFlagKWArg, fn), "bar"), kwargs: wrapKWArgs("b", "baz", "c", "qux"), want: newTestTuple("bar", newTestDict("b", "baz", "c", "qux")).ToObject()},
}
for _, cas := range cases {
if err := runInvokeTestCase(testFunc.ToObject(), &cas); err != "" {
t.Error(err)
}
}
}
...@@ -76,7 +76,7 @@ type Function struct { ...@@ -76,7 +76,7 @@ type Function struct {
Object Object
fn Func fn Func
name string `attr:"__name__"` name string `attr:"__name__"`
spec *functionSpec code *Code `attr:"func_code"`
} }
// FunctionArg describes a parameter to a Python function. // FunctionArg describes a parameter to a Python function.
...@@ -88,61 +88,19 @@ type FunctionArg struct { ...@@ -88,61 +88,19 @@ type FunctionArg struct {
Def *Object Def *Object
} }
// functionSpec describes a Python user function.
type functionSpec struct {
args []FunctionArg
fn func(*Frame, []*Object) (*Object, *BaseException)
specArgc int
numBodyArgs int
varArgIndex int
kwArgIndex int
minArgc int
}
// NewFunction creates a function object corresponding to a Python function // NewFunction creates a function object corresponding to a Python function
// described by spec. When called, the arguments are validated before calling // taking the given args, vararg and kwarg. When called, the arguments are
// spec.Fn. This includes checking that an appropriate number of arguments are // validated before calling fn. This includes checking that an appropriate
// provided, populating *args and **kwargs if necessary, etc. // number of arguments are provided, populating *args and **kwargs if
func NewFunction(name string, args []FunctionArg, vararg string, kwarg string, fn func(*Frame, []*Object) (*Object, *BaseException)) *Function { // necessary, etc.
specArgc := len(args) func NewFunction(name string, c *Code) *Function {
numBodyArgs := specArgc return &Function{Object{typ: FunctionType, dict: NewDict()}, nil, name, c}
varArgIndex, kwArgIndex := -1, -1
if vararg != "" {
varArgIndex = numBodyArgs
numBodyArgs++
}
if kwarg != "" {
kwArgIndex = numBodyArgs
numBodyArgs++
}
minArgc := 0
for ; minArgc < specArgc; minArgc++ {
if args[minArgc].Def != nil {
break
}
}
for i := minArgc; i < specArgc; i++ {
if args[i].Def == nil {
format := "%s() non-keyword arg %s after keyword arg"
logFatal(fmt.Sprintf(format, name, args[i].Name))
}
}
spec := &functionSpec{
args: args,
fn: fn,
specArgc: specArgc,
numBodyArgs: numBodyArgs,
varArgIndex: varArgIndex,
kwArgIndex: kwArgIndex,
minArgc: minArgc,
}
return &Function{Object{typ: FunctionType, dict: NewDict()}, nil, name, spec}
} }
// newBuiltinFunction returns a function object with the given name that // newBuiltinFunction returns a function object with the given name that
// invokes fn when called. // invokes fn when called.
func newBuiltinFunction(name string, fn func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException)) *Function { func newBuiltinFunction(name string, fn Func) *Function {
return &Function{Object{typ: FunctionType, dict: NewDict()}, fn, name, nil} return &Function{Object: Object{typ: FunctionType, dict: NewDict()}, fn: fn, name: name}
} }
func toFunctionUnsafe(o *Object) *Function { func toFunctionUnsafe(o *Object) *Function {
...@@ -161,64 +119,11 @@ func (f *Function) Name() string { ...@@ -161,64 +119,11 @@ func (f *Function) Name() string {
func functionCall(f *Frame, callable *Object, args Args, kwargs KWArgs) (*Object, *BaseException) { func functionCall(f *Frame, callable *Object, args Args, kwargs KWArgs) (*Object, *BaseException) {
fun := toFunctionUnsafe(callable) fun := toFunctionUnsafe(callable)
spec := fun.spec code := fun.code
if spec == nil { if code == nil {
return fun.fn(f, args, kwargs) return fun.fn(f, args, kwargs)
} }
argc := len(args) return code.call(f, args, kwargs)
if argc > spec.specArgc && spec.varArgIndex == -1 {
format := "%s() takes %d arguments (%d given)"
return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, fun.name, spec.specArgc, argc))
}
bodyArgs := f.MakeArgs(spec.numBodyArgs)
i := 0
for ; i < argc && i < spec.specArgc; i++ {
bodyArgs[i] = args[i]
}
if spec.varArgIndex != -1 {
bodyArgs[spec.varArgIndex] = NewTuple(args[i:].makeCopy()...).ToObject()
}
var kwargDict *Dict
if spec.kwArgIndex != -1 {
kwargDict = NewDict()
bodyArgs[spec.kwArgIndex] = kwargDict.ToObject()
}
for _, kw := range kwargs {
name := kw.Name
j := 0
for ; j < spec.specArgc; j++ {
if spec.args[j].Name == name {
if bodyArgs[j] != nil {
format := "%s() got multiple values for keyword argument '%s'"
return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, fun.name, name))
}
bodyArgs[j] = kw.Value
break
}
}
if j == spec.specArgc {
if kwargDict == nil {
format := "%s() got an unexpected keyword argument '%s'"
return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, fun.name, name))
}
if raised := kwargDict.SetItemString(f, name, kw.Value); raised != nil {
return nil, raised
}
}
}
for ; i < spec.specArgc; i++ {
arg := spec.args[i]
if bodyArgs[i] == nil {
if arg.Def == nil {
format := "%s() takes at least %d arguments (%d given)"
return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, fun.name, spec.minArgc, argc))
}
bodyArgs[i] = arg.Def
}
}
ret, raised := spec.fn(f, bodyArgs)
f.FreeArgs(bodyArgs)
return ret, raised
} }
func functionGet(_ *Frame, desc, instance *Object, owner *Type) (*Object, *BaseException) { func functionGet(_ *Frame, desc, instance *Object, owner *Type) (*Object, *BaseException) {
......
...@@ -19,113 +19,16 @@ import ( ...@@ -19,113 +19,16 @@ import (
"testing" "testing"
) )
func TestNewFunctionKeywordsCheck(t *testing.T) {
oldLogFatal := logFatal
defer func() { logFatal = oldLogFatal }()
var got string
logFatal = func(msg string) {
got = msg
}
NewFunction("foo", []FunctionArg{{"bar", None}, {"baz", nil}}, "", "", nil)
if want := "foo() non-keyword arg baz after keyword arg"; got != want {
t.Errorf("NewFunction logged %q, want %q", got, want)
}
}
func TestNewFunction(t *testing.T) {
fn := func(f *Frame, args []*Object) (*Object, *BaseException) {
return NewTuple(Args(args).makeCopy()...).ToObject(), nil
}
cases := []struct {
fun *Function
invokeTestCase
}{
{
NewFunction("f1", nil, "", "", fn),
invokeTestCase{want: NewTuple().ToObject()},
},
{
NewFunction("f2", []FunctionArg{{"a", nil}}, "", "", fn),
invokeTestCase{args: wrapArgs(123), want: newTestTuple(123).ToObject()},
},
{
NewFunction("f2", []FunctionArg{{"a", nil}}, "", "", fn),
invokeTestCase{kwargs: wrapKWArgs("a", "apple"), want: newTestTuple("apple").ToObject()},
},
{
NewFunction("f2", []FunctionArg{{"a", nil}}, "", "", fn),
invokeTestCase{kwargs: wrapKWArgs("b", "bear"), wantExc: mustCreateException(TypeErrorType, "f2() got an unexpected keyword argument 'b'")},
},
{
NewFunction("f2", []FunctionArg{{"a", nil}}, "", "", fn),
invokeTestCase{wantExc: mustCreateException(TypeErrorType, "f2() takes at least 1 arguments (0 given)")},
},
{
NewFunction("f2", []FunctionArg{{"a", nil}}, "", "", fn),
invokeTestCase{args: wrapArgs(1, 2, 3), wantExc: mustCreateException(TypeErrorType, "f2() takes 1 arguments (3 given)")},
},
{
NewFunction("f3", []FunctionArg{{"a", nil}, {"b", nil}}, "", "", fn),
invokeTestCase{args: wrapArgs(1, 2), want: newTestTuple(1, 2).ToObject()},
},
{
NewFunction("f3", []FunctionArg{{"a", nil}, {"b", nil}}, "", "", fn),
invokeTestCase{args: wrapArgs(1), kwargs: wrapKWArgs("b", "bear"), want: newTestTuple(1, "bear").ToObject()},
},
{
NewFunction("f3", []FunctionArg{{"a", nil}, {"b", nil}}, "", "", fn),
invokeTestCase{kwargs: wrapKWArgs("b", "bear", "a", "apple"), want: newTestTuple("apple", "bear").ToObject()},
},
{
NewFunction("f3", []FunctionArg{{"a", nil}, {"b", nil}}, "", "", fn),
invokeTestCase{args: wrapArgs(1), kwargs: wrapKWArgs("a", "alpha"), wantExc: mustCreateException(TypeErrorType, "f3() got multiple values for keyword argument 'a'")},
},
{
NewFunction("f4", []FunctionArg{{"a", nil}, {"b", None}}, "", "", fn),
invokeTestCase{args: wrapArgs(123), want: newTestTuple(123, None).ToObject()},
},
{
NewFunction("f4", []FunctionArg{{"a", nil}, {"b", None}}, "", "", fn),
invokeTestCase{args: wrapArgs(123, "bar"), want: newTestTuple(123, "bar").ToObject()},
},
{
NewFunction("f4", []FunctionArg{{"a", nil}, {"b", None}}, "", "", fn),
invokeTestCase{kwargs: wrapKWArgs("a", 123, "b", "bar"), want: newTestTuple(123, "bar").ToObject()},
},
{
NewFunction("f5", []FunctionArg{{"a", nil}}, "args", "", fn),
invokeTestCase{args: wrapArgs(1), want: newTestTuple(1, NewTuple()).ToObject()},
},
{
NewFunction("f5", []FunctionArg{{"a", nil}}, "args", "", fn),
invokeTestCase{args: wrapArgs(1, 2, 3), want: newTestTuple(1, newTestTuple(2, 3)).ToObject()},
},
{
NewFunction("f6", []FunctionArg{{"a", nil}}, "", "kwargs", fn),
invokeTestCase{args: wrapArgs("bar"), want: newTestTuple("bar", NewDict()).ToObject()},
},
{
NewFunction("f6", []FunctionArg{{"a", nil}}, "", "kwargs", fn),
invokeTestCase{kwargs: wrapKWArgs("a", "apple", "b", "bear"), want: newTestTuple("apple", newTestDict("b", "bear")).ToObject()},
},
{
NewFunction("f6", []FunctionArg{{"a", nil}}, "", "kwargs", fn),
invokeTestCase{args: wrapArgs("bar"), kwargs: wrapKWArgs("b", "baz", "c", "qux"), want: newTestTuple("bar", newTestDict("b", "baz", "c", "qux")).ToObject()},
},
}
for _, cas := range cases {
if err := runInvokeTestCase(cas.fun.ToObject(), &cas.invokeTestCase); err != "" {
t.Error(err)
}
}
}
func TestFunctionCall(t *testing.T) { func TestFunctionCall(t *testing.T) {
fun := newBuiltinFunction("fun", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { foo := newBuiltinFunction("foo", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
return newTestTuple(NewTuple(args.makeCopy()...), kwargs.makeDict()).ToObject(), nil return newTestTuple(NewTuple(args.makeCopy()...), kwargs.makeDict()).ToObject(), nil
}).ToObject() }).ToObject()
bar := NewFunction("bar", NewCode("bar", nil, CodeFlagVarArg, func(f *Frame, args []*Object) (*Object, *BaseException) {
return args[0], nil
}))
cases := []invokeTestCase{ cases := []invokeTestCase{
{args: wrapArgs(fun, 123, "abc"), kwargs: wrapKWArgs("b", "bear"), want: newTestTuple(newTestTuple(123, "abc"), newTestDict("b", "bear")).ToObject()}, {args: wrapArgs(foo, 123, "abc"), kwargs: wrapKWArgs("b", "bear"), want: newTestTuple(newTestTuple(123, "abc"), newTestDict("b", "bear")).ToObject()},
{args: wrapArgs(bar, "bar", "baz"), want: newTestTuple("bar", "baz").ToObject()},
{wantExc: mustCreateException(TypeErrorType, "unbound method __call__() must be called with function instance as first argument (got nothing instead)")}, {wantExc: mustCreateException(TypeErrorType, "unbound method __call__() must be called with function instance as first argument (got nothing instead)")},
{args: wrapArgs(newObject(ObjectType)), wantExc: mustCreateException(TypeErrorType, "unbound method __call__() must be called with function instance as first argument (got object instance instead)")}, {args: wrapArgs(newObject(ObjectType)), wantExc: mustCreateException(TypeErrorType, "unbound method __call__() must be called with function instance as first argument (got object instance instead)")},
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment