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)
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