Skip to content

Commit e85f707

Browse files
committed
Rework python function calling in preparation for polymorphism
1 parent 60dd952 commit e85f707

File tree

3 files changed

+126
-27
lines changed

3 files changed

+126
-27
lines changed

main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,10 @@ func main() {
6262
}
6363
code := obj.(*py.Code)
6464
module := py.NewModule("__main__", "", nil)
65-
err = vm.Run(module.Globals, module.Globals, code)
65+
res, err := vm.Run(module.Globals, module.Globals, code)
6666
if err != nil {
6767
log.Fatal(err)
6868
}
69+
fmt.Printf("Return = %v\n", res)
6970

7071
}

notes.txt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,53 @@ Testing
2020

2121
go build ./... && go build
2222
./gpython hello.pyc
23+
24+
Polymorphism
25+
26+
Want to try to retain I__XXX__ and M__XXX__ for speed
27+
28+
so first try
29+
30+
ok, I := obj.(.I__XXX__)
31+
if ok {
32+
res = I.M__XXX__(obj)
33+
}
34+
35+
However want to be able to look up "__XXX__" in dict
36+
37+
ok, res := obj.Type().Call0("__XXX__")
38+
ok, res := obj.Type().Call1("__XXX__", a)
39+
ok, res := obj.Type().Call2("__XXX__", b)
40+
ok is method found
41+
res is valid if found (or maybe NotImplemented)
42+
43+
Call() looks up name in methods and if found calls it either C wise or py-wise
44+
45+
Calling C-wise is easy enough, but how to call py-wise?
46+
47+
Assuming we are being called from the VM we would like to use the
48+
same VM to execute it? Or could make a new one which seems a little
49+
expensive and what is going to happen to exceptions and unwinding
50+
the stack?
51+
52+
Looks like python makes a new vm with PyEval_EvalCode so will need
53+
to make sure we store the unwinding stack frame in the exception
54+
(which is what python does I think)
55+
56+
all together
57+
58+
var ok bool
59+
var res Object
60+
61+
if ok, I := obj.(.I__XXX__); ok {
62+
res = I.M__XXX__(b)
63+
} else ok, res = obj.Type().Call1("__XXX__", b); !ok {
64+
res = NotImplemented
65+
}
66+
67+
Idea
68+
69+
could run the vm in a go routine - then could start new stack frames in existing vm from go
70+
71+
72+

vm/eval.go

Lines changed: 74 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -871,9 +871,45 @@ func (vm *Vm) NotImplemented(name string, arg int32) {
871871
fmt.Printf("%s %d NOT IMPLEMENTED\n", name, arg)
872872
}
873873

874+
// setupCall sets function fnObj up for calling with args and kwargs
875+
//
876+
// if fnObj is Go code then it calls it and returns result, nil, nil
877+
//
878+
// if fnObj requires evaluation in the vm then it returns nil, locals, globals
879+
//
880+
// kwargs should be nil if not required
881+
//
882+
// fnObj must be a callable type such as *py.Method or *py.Function
883+
//
884+
// The result is returned
885+
func setupCall(fnObj py.Object, args py.Tuple, kwargs py.StringDict) (result py.Object, locals py.StringDict, pyfn *py.Function) {
886+
// fmt.Printf("setupCall %T %v with args = %v, kwargs = %v\n", fnObj, fnObj, args, kwargs)
887+
switch fn := fnObj.(type) {
888+
case *py.Method:
889+
self := py.None // FIXME should be the module
890+
if kwargs != nil {
891+
result = fn.CallWithKeywords(self, args, kwargs)
892+
} else {
893+
result = fn.Call(self, args)
894+
}
895+
case *py.Function:
896+
pyfn = fn
897+
if kwargs != nil {
898+
locals = fn.LocalsForCallWithKeywords(args, kwargs)
899+
} else {
900+
locals = fn.LocalsForCall(args)
901+
}
902+
default:
903+
// FIXME should be TypeError
904+
panic(fmt.Sprintf("TypeError: '%s' object is not callable", fnObj.Type().Name))
905+
}
906+
return
907+
}
908+
874909
// Calls function fn with args and kwargs
875910
//
876-
// fn can be a string in which case it will be looked up or another callable type
911+
// fn can be a string in which case it will be looked up or another
912+
// callable type such as *py.Method or *py.Function
877913
//
878914
// kwargs is a sequence of name, value pairs
879915
//
@@ -891,29 +927,19 @@ func (vm *Vm) Call(fnObj py.Object, args []py.Object, kwargs []py.Object) {
891927
kwargsd[string(kwargs[i].(py.String))] = kwargs[i+1]
892928
}
893929
}
894-
try_again:
895-
switch fn := fnObj.(type) {
896-
case py.String:
930+
931+
// Lookup if string
932+
if fn, ok := fnObj.(py.String); ok {
897933
fnObj = vm.frame.Lookup(string(fn))
898-
goto try_again
899-
case *py.Method:
900-
self := py.None // FIXME should be the module
901-
if kwargsd != nil {
902-
vm.PUSH(fn.CallWithKeywords(self, args, kwargsd))
903-
} else {
904-
vm.PUSH(fn.Call(self, args))
905-
}
906-
case *py.Function:
907-
var locals py.StringDict
908-
if kwargsd != nil {
909-
locals = fn.LocalsForCallWithKeywords(args, kwargsd)
910-
} else {
911-
locals = fn.LocalsForCall(args)
912-
}
913-
vm.PushFrame(vm.frame.Globals, locals, fn.Code)
914-
default:
915-
// FIXME should be TypeError
916-
panic(fmt.Sprintf("TypeError: '%s' object is not callable", fnObj.Type().Name))
934+
}
935+
936+
// Call or setup for the call
937+
result, locals, fn := setupCall(fnObj, args, kwargsd)
938+
// fmt.Printf("setupCall returned %v, %v, %v\n", result, locals, fn)
939+
if fn == nil {
940+
vm.PUSH(result)
941+
} else {
942+
vm.PushFrame(fn.Globals, locals, fn.Code)
917943
}
918944
}
919945

@@ -944,7 +970,9 @@ func (vm *Vm) PopFrame() {
944970
// FIXME figure out how we are going to signal exceptions!
945971
//
946972
// Any parameters are expected to have been decoded into locals
947-
func Run(globals, locals py.StringDict, code *py.Code) (err error) {
973+
//
974+
// Returns an Object and an error
975+
func Run(globals, locals py.StringDict, code *py.Code) (res py.Object, err error) {
948976
vm := NewVm()
949977
defer func() {
950978
if r := recover(); r != nil {
@@ -988,6 +1016,26 @@ func Run(globals, locals py.StringDict, code *py.Code) (err error) {
9881016
fmt.Printf("vmstack = %#v\n", vm.stack)
9891017
panic("vm stack should only have 1 entry on at this point")
9901018
}
991-
// return vm.POP()
992-
return nil
1019+
return vm.POP(), nil
1020+
}
1021+
1022+
// Calls function fnObj with args and kwargs in a new vm (or directly
1023+
// if Go code)
1024+
//
1025+
// kwargs should be nil if not required
1026+
//
1027+
// fnObj must be a callable type such as *py.Method or *py.Function
1028+
//
1029+
// The result is returned
1030+
func Call(fnObj py.Object, args py.Tuple, kwargs py.StringDict) py.Object {
1031+
result, locals, fn := setupCall(fnObj, args, kwargs)
1032+
if result == nil {
1033+
var err error
1034+
result, err = Run(fn.Globals, locals, fn.Code)
1035+
if err != nil {
1036+
// FIXME - do what exactly!
1037+
panic(err)
1038+
}
1039+
}
1040+
return result
9931041
}

0 commit comments

Comments
 (0)