Skip to content

Commit 114c283

Browse files
committed
vm: functions & DELETE_NAME, DELETE_GLOBAL, LOAD_NAME, LOAD_GLOBAL, STORE_DEREF, DELETE_DEREF
1 parent f570f95 commit 114c283

File tree

5 files changed

+237
-31
lines changed

5 files changed

+237
-31
lines changed

py/cell.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,8 @@ func (c *Cell) Get() Object {
3434
func (c *Cell) Set(obj Object) {
3535
c.obj = &obj
3636
}
37+
38+
// Delete the contents of the Cell
39+
func (c *Cell) Delete() {
40+
c.obj = nil
41+
}

py/frame.go

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,20 +87,11 @@ func NewFrame(globals, locals StringDict, code *Code, closure Tuple) *Frame {
8787
}
8888
}
8989

90-
// Python names are looked up in three scopes
90+
// Python globals are looked up in two scopes
9191
//
92-
// First the local scope
93-
// Next the module global scope
92+
// The module global scope
9493
// And finally the builtins
95-
func (f *Frame) Lookup(name string) (obj Object) {
96-
var ok bool
97-
98-
// Lookup in locals
99-
// fmt.Printf("locals = %v\n", f.Locals)
100-
if obj, ok = f.Locals[name]; ok {
101-
return
102-
}
103-
94+
func (f *Frame) LookupGlobal(name string) (obj Object, ok bool) {
10495
// Lookup in globals
10596
// fmt.Printf("globals = %v\n", f.Globals)
10697
if obj, ok = f.Globals[name]; ok {
@@ -113,7 +104,22 @@ func (f *Frame) Lookup(name string) (obj Object) {
113104
return
114105
}
115106

116-
panic(ExceptionNewf(NameError, "name '%s' is not defined", name))
107+
return nil, false
108+
}
109+
110+
// Python names are looked up in three scopes
111+
//
112+
// First the local scope
113+
// Next the module global scope
114+
// And finally the builtins
115+
func (f *Frame) Lookup(name string) (obj Object, ok bool) {
116+
// Lookup in locals
117+
// fmt.Printf("locals = %v\n", f.Locals)
118+
if obj, ok = f.Locals[name]; ok {
119+
return
120+
}
121+
122+
return f.LookupGlobal(name)
117123
}
118124

119125
// Make a new Block (try/for/while)

vm/eval.go

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ package vm
44
// FIXME use LocalVars instead of storing everything in the Locals dict
55
// see frameobject.c dict_to_map and LocalsToFast
66

7+
// FIXME *_FAST aren't using the fast path
8+
79
/* FIXME
810
911
cpython has one stack per frame, not one stack in total
@@ -32,7 +34,6 @@ objects so they can be GCed
3234
*/
3335

3436
import (
35-
// "fmt"
3637
"runtime/debug"
3738

3839
"github.com/ncw/gpython/py"
@@ -749,7 +750,12 @@ func do_STORE_NAME(vm *Vm, namei int32) {
749750
// attribute of the code object.
750751
func do_DELETE_NAME(vm *Vm, namei int32) {
751752
defer vm.CheckException()
752-
vm.NotImplemented("DELETE_NAME", namei)
753+
name := vm.frame.Code.Names[namei]
754+
if _, ok := vm.frame.Locals[name]; !ok {
755+
vm.SetException(py.ExceptionNewf(py.NameError, nameErrorMsg, name))
756+
} else {
757+
delete(vm.frame.Locals, name)
758+
}
753759
}
754760

755761
// Unpacks TOS into count individual values, which are put onto the
@@ -798,7 +804,12 @@ func do_STORE_GLOBAL(vm *Vm, namei int32) {
798804
// Works as DELETE_NAME, but deletes a global name.
799805
func do_DELETE_GLOBAL(vm *Vm, namei int32) {
800806
defer vm.CheckException()
801-
vm.NotImplemented("DELETE_GLOBAL", namei)
807+
name := vm.frame.Code.Names[namei]
808+
if _, ok := vm.frame.Globals[name]; !ok {
809+
vm.SetException(py.ExceptionNewf(py.NameError, nameErrorMsg, name))
810+
} else {
811+
delete(vm.frame.Globals, name)
812+
}
802813
}
803814

804815
// Pushes co_consts[consti] onto the stack.
@@ -811,8 +822,14 @@ func do_LOAD_CONST(vm *Vm, consti int32) {
811822
// Pushes the value associated with co_names[namei] onto the stack.
812823
func do_LOAD_NAME(vm *Vm, namei int32) {
813824
defer vm.CheckException()
814-
debugf("LOAD_NAME %v\n", vm.frame.Code.Names[namei])
815-
vm.PUSH(vm.frame.Lookup(vm.frame.Code.Names[namei]))
825+
name := vm.frame.Code.Names[namei]
826+
debugf("LOAD_NAME %v\n", name)
827+
obj, ok := vm.frame.Lookup(name)
828+
if !ok {
829+
vm.SetException(py.ExceptionNewf(py.NameError, nameErrorMsg, name))
830+
} else {
831+
vm.PUSH(obj)
832+
}
816833
}
817834

818835
// Creates a tuple consuming count items from the stack, and pushes
@@ -1023,9 +1040,14 @@ func do_FOR_ITER(vm *Vm, delta int32) {
10231040
// Loads the global named co_names[namei] onto the stack.
10241041
func do_LOAD_GLOBAL(vm *Vm, namei int32) {
10251042
defer vm.CheckException()
1026-
// FIXME this is looking in local scope too - is that correct?
1027-
debugf("LOAD_GLOBAL %v\n", vm.frame.Code.Names[namei])
1028-
vm.PUSH(vm.frame.Lookup(vm.frame.Code.Names[namei]))
1043+
name := vm.frame.Code.Names[namei]
1044+
debugf("LOAD_GLOBAL %v\n", name)
1045+
obj, ok := vm.frame.LookupGlobal(name)
1046+
if !ok {
1047+
vm.SetException(py.ExceptionNewf(py.NameError, nameErrorMsg, name))
1048+
} else {
1049+
vm.PUSH(obj)
1050+
}
10291051
}
10301052

10311053
// Pushes a block for a loop onto the block stack. The block spans
@@ -1069,7 +1091,9 @@ func do_LOAD_FAST(vm *Vm, var_num int32) {
10691091
if value, ok := vm.frame.Locals[varname]; ok {
10701092
vm.PUSH(value)
10711093
} else {
1072-
vm.SetException(py.ExceptionNewf(py.UnboundLocalError, unboundLocalErrorMsg, varname))
1094+
vm.SetException(py.ExceptionNewf(py.NameError, nameErrorMsg, varname))
1095+
// FIXME ceval.c says this, but it python3.4 returns the above
1096+
// vm.SetException(py.ExceptionNewf(py.UnboundLocalError, unboundLocalErrorMsg, varname))
10731097
}
10741098
}
10751099

@@ -1086,7 +1110,8 @@ func do_DELETE_FAST(vm *Vm, var_num int32) {
10861110
if _, ok := vm.frame.Locals[varname]; ok {
10871111
delete(vm.frame.Locals, varname)
10881112
} else {
1089-
vm.SetException(py.ExceptionNewf(py.UnboundLocalError, unboundLocalErrorMsg, varname))
1113+
vm.SetException(py.ExceptionNewf(py.NameError, nameErrorMsg, varname))
1114+
// FIXME ceval.c says this vm.SetException(py.ExceptionNewf(py.UnboundLocalError, unboundLocalErrorMsg, varname))
10901115
}
10911116
}
10921117

@@ -1111,19 +1136,24 @@ func do_LOAD_CLOSURE(vm *Vm, i int32) {
11111136
vm.PUSH(vm.frame.CellAndFreeVars[i])
11121137
}
11131138

1139+
// writes the correct errors for an unbound deref
1140+
func unboundDeref(vm *Vm, i int32) {
1141+
varname, free := _var_name(vm, i)
1142+
if free {
1143+
vm.SetException(py.ExceptionNewf(py.NameError, unboundFreeErrorMsg, varname))
1144+
} else {
1145+
vm.SetException(py.ExceptionNewf(py.UnboundLocalError, unboundLocalErrorMsg, varname))
1146+
}
1147+
}
1148+
11141149
// Loads the cell contained in slot i of the cell and free variable
11151150
// storage. Pushes a reference to the object the cell contains on the
11161151
// stack.
11171152
func do_LOAD_DEREF(vm *Vm, i int32) {
11181153
defer vm.CheckException()
11191154
res := vm.frame.CellAndFreeVars[i].(*py.Cell).Get()
11201155
if res == nil {
1121-
varname, free := _var_name(vm, i)
1122-
if free {
1123-
vm.SetException(py.ExceptionNewf(py.UnboundLocalError, unboundFreeErrorMsg, varname))
1124-
} else {
1125-
vm.SetException(py.ExceptionNewf(py.UnboundLocalError, unboundLocalErrorMsg, varname))
1126-
}
1156+
unboundDeref(vm, i)
11271157
}
11281158
vm.PUSH(res)
11291159
}
@@ -1140,14 +1170,19 @@ func do_LOAD_CLASSDEREF(vm *Vm, i int32) {
11401170
// variable storage.
11411171
func do_STORE_DEREF(vm *Vm, i int32) {
11421172
defer vm.CheckException()
1143-
vm.NotImplemented("STORE_DEREF", i)
1173+
cell := vm.frame.CellAndFreeVars[i].(*py.Cell)
1174+
cell.Set(vm.POP())
11441175
}
11451176

11461177
// Empties the cell contained in slot i of the cell and free variable
11471178
// storage. Used by the del statement.
11481179
func do_DELETE_DEREF(vm *Vm, i int32) {
11491180
defer vm.CheckException()
1150-
vm.NotImplemented("DELETE_DEREF", i)
1181+
cell := vm.frame.CellAndFreeVars[i].(*py.Cell)
1182+
if cell.Get() == nil {
1183+
unboundDeref(vm, i)
1184+
}
1185+
cell.Delete()
11511186
}
11521187

11531188
// Logic for the raise statement

vm/tests/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Tests for VM
2+
============
3+
4+
These simple programs are designed to exercise the VM.
5+
6+
They should run with no errors raised.
7+
8+
They should also all run clean with python3.4

vm/tests/functions.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#!/usr/bin/env python3.4
2+
3+
# Test functions
4+
def fn():
5+
return 1
6+
assert fn() == 1
7+
8+
def fn1(x):
9+
return x+1
10+
assert fn1(1) == 2
11+
12+
def fn2(x,y=1):
13+
return x+y
14+
assert fn2(1) == 2
15+
assert fn2(1,3) == 4
16+
# FIXME not implemented assert fn2(1,y=4) == 5
17+
18+
# FIXME check *arg and **kwarg
19+
20+
21+
# Closure
22+
23+
# FIXME something wrong with closures over function arguments...
24+
# def counter3(x):
25+
# def inc():
26+
# nonlocal x
27+
# x += 1
28+
# return x
29+
# return inc
30+
# fn3 = counter3(1)
31+
# assert fn3() == 2
32+
# assert fn3() == 3
33+
34+
def counter4(initial):
35+
x = initial
36+
def inc():
37+
nonlocal x
38+
x += 1
39+
return x
40+
return inc
41+
fn4 = counter4(1)
42+
assert fn4() == 2
43+
assert fn4() == 3
44+
45+
def counter5(initial):
46+
L = [initial]
47+
def inc():
48+
L[0] += 1
49+
return L[0]
50+
return inc
51+
fn5 = counter5(1)
52+
assert fn5() == 2
53+
assert fn5() == 3
54+
55+
56+
def del_deref6(initial):
57+
x = initial
58+
def inc():
59+
nonlocal x
60+
a = x
61+
del x
62+
return a+1
63+
return inc
64+
fn6 = del_deref6(1)
65+
assert fn6() == 2
66+
try:
67+
fn6()
68+
except NameError as e:
69+
# FIXME assert str(e) == "free variable 'x' referenced before assignment in enclosing scope"
70+
pass
71+
else:
72+
assert False, "NameError not raised"
73+
74+
# check you can't delete it twice!
75+
76+
def fn7(b):
77+
c = 1
78+
def nested(d):
79+
nonlocal c
80+
del c
81+
del c
82+
return nested
83+
84+
try:
85+
fn7(1)(2)
86+
except NameError as e:
87+
# FIXME assert str(e) == "free variable 'c' referenced before assignment in enclosing scope"
88+
pass
89+
else:
90+
assert False, "NameError not raised"
91+
92+
# globals
93+
94+
a = 1
95+
def fn8():
96+
global a
97+
assert a == 1
98+
a = 2
99+
assert a == 2
100+
fn8()
101+
assert a == 2
102+
def fn9():
103+
global a
104+
del a
105+
fn9()
106+
try:
107+
a
108+
except NameError as e:
109+
# FIXME assert str(e) == "name 'a' is not defined"
110+
pass
111+
else:
112+
assert False, "NameError not raised"
113+
try:
114+
fn9()
115+
except NameError as e:
116+
# FIXME assert str(e) == "name 'a' is not defined"
117+
pass
118+
else:
119+
assert False, "NameError not raised"
120+
121+
# delete
122+
def fn10():
123+
a = 1
124+
assert a == 1
125+
del a
126+
try:
127+
a
128+
except NameError as e:
129+
# FIXME assert str(e) == "name 'a' is not defined"
130+
pass
131+
else:
132+
assert False, "NameError not raised"
133+
try:
134+
del a
135+
except NameError as e:
136+
# FIXME assert str(e) == "name 'a' is not defined"
137+
pass
138+
else:
139+
assert False, "NameError not raised"
140+
fn10()
141+
142+
# annotations
143+
def fn11(a:"A") -> "RET":
144+
return a+1
145+
assert fn11(1) == 2
146+
# FIXME check annotations are in place
147+
148+
#kwargs
149+
def fn12(*args,a=2,b=3,**kwargs) -> "RET":
150+
return args
151+
# FIXME this blows up: assert fn12() == ()
152+
# FIXME check kwargs passing

0 commit comments

Comments
 (0)