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(objObject){
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(namestring) (objObject){
96-
varokbool
97-
98-
// Lookup in locals
99-
// fmt.Printf("locals = %v\n", f.Locals)
100-
ifobj, ok=f.Locals[name]; ok{
101-
return
102-
}
103-
94+
func (f*Frame) LookupGlobal(namestring) (objObject, okbool){
10495
// Lookup in globals
10596
// fmt.Printf("globals = %v\n", f.Globals)
10697
ifobj, 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+
returnnil, 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(namestring) (objObject, okbool){
116+
// Lookup in locals
117+
// fmt.Printf("locals = %v\n", f.Locals)
118+
ifobj, ok=f.Locals[name]; ok{
119+
return
120+
}
121+
122+
returnf.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
funcdo_DELETE_NAME(vm*Vm, nameiint32){
751752
defervm.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
funcdo_DELETE_GLOBAL(vm*Vm, nameiint32){
800806
defervm.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
funcdo_LOAD_NAME(vm*Vm, nameiint32){
813824
defervm.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
funcdo_LOAD_GLOBAL(vm*Vm, nameiint32){
10251042
defervm.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
ifvalue, 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+
funcunboundDeref(vm*Vm, iint32){
1141+
varname, free:=_var_name(vm, i)
1142+
iffree{
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
funcdo_LOAD_DEREF(vm*Vm, iint32){
11181153
defervm.CheckException()
11191154
res:=vm.frame.CellAndFreeVars[i].(*py.Cell).Get()
11201155
ifres==nil{
1121-
varname, free:=_var_name(vm, i)
1122-
iffree{
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
funcdo_STORE_DEREF(vm*Vm, iint32){
11421172
defervm.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
funcdo_DELETE_DEREF(vm*Vm, iint32){
11491180
defervm.CheckException()
1150-
vm.NotImplemented("DELETE_DEREF", i)
1181+
cell:=vm.frame.CellAndFreeVars[i].(*py.Cell)
1182+
ifcell.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+
deffn():
5+
return1
6+
assertfn() ==1
7+
8+
deffn1(x):
9+
returnx+1
10+
assertfn1(1) ==2
11+
12+
deffn2(x,y=1):
13+
returnx+y
14+
assertfn2(1) ==2
15+
assertfn2(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+
defcounter4(initial):
35+
x=initial
36+
definc():
37+
nonlocalx
38+
x+=1
39+
returnx
40+
returninc
41+
fn4=counter4(1)
42+
assertfn4() ==2
43+
assertfn4() ==3
44+
45+
defcounter5(initial):
46+
L= [initial]
47+
definc():
48+
L[0] +=1
49+
returnL[0]
50+
returninc
51+
fn5=counter5(1)
52+
assertfn5() ==2
53+
assertfn5() ==3
54+
55+
56+
defdel_deref6(initial):
57+
x=initial
58+
definc():
59+
nonlocalx
60+
a=x
61+
delx
62+
returna+1
63+
returninc
64+
fn6=del_deref6(1)
65+
assertfn6() ==2
66+
try:
67+
fn6()
68+
exceptNameErrorase:
69+
# FIXME assert str(e) == "free variable 'x' referenced before assignment in enclosing scope"
70+
pass
71+
else:
72+
assertFalse, "NameError not raised"
73+
74+
# check you can't delete it twice!
75+
76+
deffn7(b):
77+
c=1
78+
defnested(d):
79+
nonlocalc
80+
delc
81+
delc
82+
returnnested
83+
84+
try:
85+
fn7(1)(2)
86+
exceptNameErrorase:
87+
# FIXME assert str(e) == "free variable 'c' referenced before assignment in enclosing scope"
88+
pass
89+
else:
90+
assertFalse, "NameError not raised"
91+
92+
# globals
93+
94+
a=1
95+
deffn8():
96+
globala
97+
asserta==1
98+
a=2
99+
asserta==2
100+
fn8()
101+
asserta==2
102+
deffn9():
103+
globala
104+
dela
105+
fn9()
106+
try:
107+
a
108+
exceptNameErrorase:
109+
# FIXME assert str(e) == "name 'a' is not defined"
110+
pass
111+
else:
112+
assertFalse, "NameError not raised"
113+
try:
114+
fn9()
115+
exceptNameErrorase:
116+
# FIXME assert str(e) == "name 'a' is not defined"
117+
pass
118+
else:
119+
assertFalse, "NameError not raised"
120+
121+
# delete
122+
deffn10():
123+
a=1
124+
asserta==1
125+
dela
126+
try:
127+
a
128+
exceptNameErrorase:
129+
# FIXME assert str(e) == "name 'a' is not defined"
130+
pass
131+
else:
132+
assertFalse, "NameError not raised"
133+
try:
134+
dela
135+
exceptNameErrorase:
136+
# FIXME assert str(e) == "name 'a' is not defined"
137+
pass
138+
else:
139+
assertFalse, "NameError not raised"
140+
fn10()
141+
142+
# annotations
143+
deffn11(a:"A") ->"RET":
144+
returna+1
145+
assertfn11(1) ==2
146+
# FIXME check annotations are in place
147+
148+
#kwargs
149+
deffn12(*args,a=2,b=3,**kwargs) ->"RET":
150+
returnargs
151+
# FIXME this blows up: assert fn12() == ()
152+
# FIXME check kwargs passing

0 commit comments

Comments
(0)