Skip to content

Commit 08446c2

Browse files
committed
symtable: fix functions, add classes, lambda, increase coverage
1 parent efe2b0e commit 08446c2

File tree

4 files changed

+1397
-293
lines changed

4 files changed

+1397
-293
lines changed

symtable/make_symtable_test.py

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
('''def fn(a,b):\n e=1\n return a*b*c*d*e''', "exec"),
2626
('''def fn(a,b):\n def nested(c,d):\n return a*b*c*d*e''', "exec"),
2727
('''\
28-
def fn(a:"a",*arg:"arg",b:"b"=1,c:"c"=2,**kwargs:"kw") -> "ret":
28+
def fn(a:A,*arg:ARG,b:B=BB,c:C=CC,**kwargs:KW) -> RET:
2929
def fn(A,b):
3030
e=1
3131
return a*arg*b*c*kwargs*A*e*glob''', "exec"),
@@ -34,21 +34,86 @@ def fn(a):
3434
global b
3535
b = a''', "exec"),
3636
('''\
37+
def fn(a):
38+
global b
39+
global b
40+
return b''', "exec"),
41+
('''\
42+
def inner():
43+
print(x)
44+
global x
45+
''', "exec"),
46+
('''\
3747
def fn(a):
3848
b = 6
3949
global b
4050
b = a''', "exec"),
4151
('''\
52+
def fn(a=b,c=1):
53+
return a+b''', "exec"),
54+
('''\
55+
@sausage
56+
@potato(beans)
4257
def outer():
4358
x = 1
4459
def inner():
4560
nonlocal x
4661
x = 2''', "exec"),
4762
('''\
63+
def fn(a):
64+
nonlocal b
65+
''', "exec", SyntaxError),
66+
('''\
4867
def outer():
4968
def inner():
5069
nonlocal x
5170
x = 2''', "exec", SyntaxError),
71+
('''\
72+
def outer():
73+
x = 1
74+
def inner():
75+
print(x)
76+
nonlocal x
77+
''', "exec"),
78+
('''\
79+
def outer():
80+
x = 1
81+
def inner():
82+
x = 2
83+
nonlocal x''', "exec"),
84+
('''\
85+
def outer():
86+
x = 1
87+
def inner(x):
88+
nonlocal x''', "exec", SyntaxError),
89+
('''\
90+
def outer():
91+
x = 1
92+
def inner(x):
93+
global x''', "exec", SyntaxError),
94+
('''\
95+
def outer():
96+
def inner():
97+
global x
98+
nonlocal x
99+
''', "exec", SyntaxError),
100+
('''\
101+
def outer():
102+
x = 1
103+
def inner():
104+
y = 2
105+
return x + y + z
106+
''', "exec"),
107+
('''\
108+
def outer():
109+
global x
110+
def inner():
111+
return x
112+
''', "exec"),
113+
('''\
114+
nonlocal x
115+
''', "exec", SyntaxError),
116+
('''def fn(a,a): pass''', "exec", SyntaxError),
52117
# List Comp
53118
('''[ x for x in xs ]''', "exec"),
54119
('''[ x+y for x in xs for y in ys ]''', "exec"),
@@ -70,7 +135,44 @@ def inner():
70135
('''{ x+y:1 for x in xs for y in ys }''', "exec"),
71136
('''{ x+y+z:1 for x in xs if x if y if z if r for y in ys if x if y if z if p for z in zs if x if y if z if q}''', "exec"),
72137
('''{ x+y:k for k, x in { x:1 for x in xs } }''', "exec"),
73-
# FIXME need with x as y
138+
# Class
139+
('''\
140+
@potato
141+
@sausage()
142+
class A(a,b,c="1",d="2",*args,**kwargs):
143+
VAR = x
144+
def method(self):
145+
super().method()
146+
return VAR
147+
''', "exec"),
148+
# Lambda
149+
('''lambda: x''', "exec"),
150+
('''lambda y: x+y''', "exec"),
151+
('''lambda a,*arg,b=BB,c=CC,**kwargs: POTATO+a+arg+b+c+kwargs''', "exec"),
152+
# With
153+
('''\
154+
with x() as y:
155+
y.floop()
156+
print(y)
157+
''', "exec"),
158+
# try, except
159+
('''\
160+
try:
161+
something()
162+
except RandomError as e:
163+
print(e)
164+
print(e)
165+
''', "exec"),
166+
# Import
167+
('''import potato''', "exec"),
168+
('''import potato.sausage''', "exec"),
169+
('''from potato import sausage''', "exec"),
170+
('''from potato import sausage as salami''', "exec"),
171+
('''from potato import *''', "exec"),
172+
('''\
173+
def fn():
174+
from potato import *
175+
''', "exec", SyntaxError),
74176
]
75177

76178
def dump_bool(b):

symtable/symtable.go

Lines changed: 92 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ type SymTable struct {
105105
// col_offset int // offset of first line of block
106106
// opt_lineno int // lineno of last exec or import *
107107
// opt_col_offset int // offset of last exec or import *
108-
TmpName int // counter for listcomp temp vars
108+
TmpName int // counter for listcomp temp vars
109+
Private string // name of current class or ""
109110

110111
Symbols Symbols
111112
Global *SymTable // symbol table entry for module
@@ -154,15 +155,32 @@ func newSymTableBlock(Ast ast.Ast, Type BlockType, Name string, parent *SymTable
154155
return stNew
155156
}
156157

158+
// Add arguments to the symbol table
159+
func (st *SymTable) addArgumentsToSymbolTable(node *ast.Arguments) {
160+
if node.Args == nil {
161+
return
162+
}
163+
// skip default arguments inside function block
164+
// XXX should ast be different?
165+
for _, arg := range node.Args {
166+
st.AddDef(arg.Arg, defParam)
167+
}
168+
for _, arg := range node.Kwonlyargs {
169+
st.AddDef(arg.Arg, defParam)
170+
}
171+
if node.Vararg != nil {
172+
st.AddDef(node.Vararg.Arg, defParam)
173+
st.Varargs = true
174+
}
175+
if node.Kwarg != nil {
176+
st.AddDef(node.Kwarg.Arg, defParam)
177+
st.Varkeywords = true
178+
}
179+
}
180+
157181
// Parse the ast into the symbol table
158182
func (st *SymTable) Parse(Ast ast.Ast) {
159183
ast.Walk(Ast, func(Ast ast.Ast) bool {
160-
// New symbol tables needed at these points
161-
// FunctionDef
162-
// ClassDef
163-
// Lambda
164-
// Comprehension (all types of comprehension in py3)
165-
166184
switch node := Ast.(type) {
167185
case *ast.Nonlocal:
168186
for _, name := range node.Names {
@@ -211,19 +229,22 @@ func (st *SymTable) Parse(Ast ast.Ast) {
211229
st.AddDef(node.Name, defLocal)
212230
name := string(node.Name)
213231

214-
// Make a new symtable
215-
stNew := newSymTableBlock(Ast, FunctionBlock, name, st)
216-
217-
// Walk the Decorators and Returns in this Symtable
232+
// Walk these things in original symbol table
233+
if node.Args != nil {
234+
st.Parse(node.Args)
235+
}
218236
for _, expr := range node.DecoratorList {
219237
st.Parse(expr)
220238
}
221239
st.Parse(node.Returns)
222240

223-
// Walk the Args and Body in the new symtable
224-
if node.Args != nil {
225-
stNew.Parse(node.Args)
226-
}
241+
// Make a new symtable
242+
stNew := newSymTableBlock(Ast, FunctionBlock, name, st)
243+
244+
// Add the arguments to the new symbol table
245+
stNew.addArgumentsToSymbolTable(node.Args)
246+
247+
// Walk the Body in the new symtable
227248
for _, stmt := range node.Body {
228249
stNew.Parse(stmt)
229250
}
@@ -232,9 +253,49 @@ func (st *SymTable) Parse(Ast ast.Ast) {
232253
return false
233254
case *ast.ClassDef:
234255
st.AddDef(node.Name, defLocal)
256+
name := string(node.Name)
257+
// Parse in the original symtable
258+
for _, expr := range node.Bases {
259+
st.Parse(expr)
260+
}
261+
for _, keyword := range node.Keywords {
262+
st.Parse(keyword)
263+
}
264+
if node.Starargs != nil {
265+
st.Parse(node.Starargs)
266+
}
267+
if node.Kwargs != nil {
268+
st.Parse(node.Kwargs)
269+
}
270+
for _, expr := range node.DecoratorList {
271+
st.Parse(expr)
272+
}
273+
// Make a new symtable
274+
stNew := newSymTableBlock(Ast, ClassBlock, name, st)
275+
stNew.Private = name // set name of class
276+
// Parse body in new symtable
277+
for _, stmt := range node.Body {
278+
stNew.Parse(stmt)
279+
}
280+
// return false to stop the parse
281+
return false
235282
case *ast.Lambda:
283+
// Parse in the original symtable
284+
if node.Args != nil {
285+
st.Parse(node.Args)
286+
}
287+
288+
// Make a new symtable
289+
stNew := newSymTableBlock(Ast, FunctionBlock, "lambda", st)
236290

237-
// Comprehensions
291+
// Add the arguments to the new symbol table
292+
stNew.addArgumentsToSymbolTable(node.Args)
293+
294+
// Walk the Body in the new symtable
295+
stNew.Parse(node.Body)
296+
297+
// return false to stop the parse
298+
return false
238299
case *ast.ListComp:
239300
st.parseComprehension(Ast, "listcomp", node.Generators, node.Elt, nil)
240301
return false
@@ -247,30 +308,10 @@ func (st *SymTable) Parse(Ast ast.Ast) {
247308
case *ast.GeneratorExp:
248309
st.parseComprehension(Ast, "genexpr", node.Generators, node.Elt, nil)
249310
return false
250-
251-
case *ast.Arguments:
252-
// skip default arguments inside function block
253-
// XXX should ast be different?
254-
for _, arg := range node.Args {
255-
st.AddDef(arg.Arg, defParam)
256-
}
257-
for _, arg := range node.Kwonlyargs {
258-
st.AddDef(arg.Arg, defParam)
259-
}
260-
if node.Vararg != nil {
261-
st.AddDef(node.Vararg.Arg, defParam)
262-
st.Varargs = true
263-
}
264-
if node.Kwarg != nil {
265-
st.AddDef(node.Kwarg.Arg, defParam)
266-
st.Varkeywords = true
267-
}
268-
269311
case *ast.ExceptHandler:
270312
if node.Name != "" {
271313
st.AddDef(node.Name, defLocal)
272314
}
273-
274315
case *ast.Alias:
275316
// Compute store_name, the name actually bound by the import
276317
// operation. It is different than node.name when node.name is a
@@ -282,7 +323,7 @@ func (st *SymTable) Parse(Ast ast.Ast) {
282323
dot := strings.LastIndex(string(name), ".")
283324
store_name := name
284325
if dot >= 0 {
285-
store_name = name[dot+1:]
326+
store_name = name[:dot]
286327
}
287328
if name != "*" {
288329
st.AddDef(store_name, defImport)
@@ -292,7 +333,6 @@ func (st *SymTable) Parse(Ast ast.Ast) {
292333
}
293334
st.Unoptimized |= optImportStar
294335
}
295-
296336
case *ast.Return:
297337
if node.Value != nil {
298338
st.ReturnsValue = true
@@ -342,18 +382,16 @@ func (st *SymTable) parseComprehension(Ast ast.Ast, scopeName string, generators
342382
stNew.Parse(elt)
343383
}
344384

345-
const duplicateArgument = "duplicate argument %q in function definition"
346-
347385
// Add a symbol into the symble table
348386
func (st *SymTable) AddDef(name ast.Identifier, flags DefUse) {
349-
// FIXME mangled := _Py_Mangle(st.st_private, name)
387+
// FIXME mangled := _Py_Mangle(st.Private, name)
350388
mangled := string(name)
351389

352390
// Add or update the symbol in the Symbols
353391
if sym, ok := st.Symbols[mangled]; ok {
354392
if (flags&defParam) != 0 && (sym.Flags&defParam) != 0 {
355393
// Is it better to use 'mangled' or 'name' here?
356-
panic(py.ExceptionNewf(py.SyntaxError, duplicateArgument, name))
394+
panic(py.ExceptionNewf(py.SyntaxError, "duplicate argument '%s' in function definition", name))
357395
// FIXME
358396
// PyErr_SyntaxLocationObject(st.st_filename,
359397
// st.st_cur.ste_lineno,
@@ -574,6 +612,14 @@ func (st *SymTable) CheckUnoptimized() {
574612
return
575613
}
576614

615+
// FIXME Acording to this
616+
// https://docs.python.org/3/whatsnew/3.0.html#removed-syntax
617+
//
618+
// The from module import * syntax is only allowed at the
619+
// module level, no longer inside functions.
620+
//
621+
// So I think this is dead code
622+
577623
trailer := "contains a nested function with free variables"
578624
if !st.ChildFree {
579625
trailer = "is a nested function"
@@ -588,7 +634,7 @@ func (st *SymTable) CheckUnoptimized() {
588634
}
589635
}
590636

591-
/* Enter the final scope information into the ste_symbols dict.
637+
/* Enter the final scope information into the st.Symbols dict.
592638
*
593639
* All arguments are dicts. Modifies symbols, others are read-only.
594640
*/
@@ -601,6 +647,9 @@ func (symbols Symbols) Update(scopes Scopes, bound, free StringSet, classflag bo
601647

602648
/* Record not yet resolved free variables from children (if any) */
603649
for name := range free {
650+
// FIXME haven't managed to find a test case for this code
651+
// suspect a problem!
652+
604653
/* Handle symbol that already exists in this scope */
605654
if symbol, ok := symbols[name]; ok {
606655
/* Handle a free variable in a method of
@@ -643,12 +692,6 @@ func (symbols Symbols) Update(scopes Scopes, bound, free StringSet, classflag bo
643692
propagate back to a parent block.
644693
*/
645694
func (st *SymTable) AnalyzeBlock(bound, free, global StringSet) {
646-
// PyObject *name, *v, *local = nil, *scopes = nil, *newbound = nil;
647-
// PyObject *newglobal = nil, *newfree = nil, *allfree = nil;
648-
// PyObject *temp;
649-
// int i, success = 0;
650-
// Py_ssize_t pos = 0;
651-
652695
local := make(StringSet) // collect new names bound in block
653696
scopes := make(Scopes) // collect scopes defined for each name
654697

0 commit comments

Comments
 (0)