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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy