This commit is contained in:
Todd Gamblin 2023-02-18 17:51:40 -08:00
parent 3b859363cb
commit a8a776b5b7

79
test.py
View File

@ -3,7 +3,8 @@
import ast import ast
import contextlib import contextlib
from typing import Dict, List from io import StringIO
from typing import Dict, List, Optional
import spack.directives import spack.directives
import spack.repo import spack.repo
@ -23,8 +24,12 @@ def is_directive(node):
class NameDescriptor(object): class NameDescriptor(object):
"""Name in a scope, with global/nonlocal and const information.""" """Name in a scope, with global/nonlocal and const information."""
def __init__(self, name, const, isglobal, isnonlocal): name: str
# type: (str, bool, bool, bool) -> None const: bool
isglobal: bool
isnonlocal: bool
def __init__(self, name: str, const: bool, isglobal: bool, isnonlocal: bool):
self.name = name self.name = name
self.const = const self.const = const
self.isglobal = isglobal self.isglobal = isglobal
@ -38,8 +43,10 @@ class ScopeStack(object):
""" """
scopes: List[Dict[str, NameDescriptor]]
def __init__(self): def __init__(self):
self.scopes = [] # type: List[Dict[str, NameDescriptor]] self.scopes = []
def push(self, name): def push(self, name):
"""Add a scope with a name for debugging.""" """Add a scope with a name for debugging."""
@ -66,16 +73,15 @@ def top(self):
_, scope = self.scopes[-1] _, scope = self.scopes[-1]
return scope return scope
def define(self, name, const=None, isglobal=None, isnonlocal=None): def define(self, name: str, const=None, isglobal=None, isnonlocal=None):
# type: NameDescriptor -> None # type: NameDescriptor -> None
self.top[name] = NameDescriptor(name, const, isglobal, isnonlocal) self.top[name] = NameDescriptor(name, const, isglobal, isnonlocal)
def assign(self, name, const=None, isglobal=None, isnonlocal=None): def assign(self, name: str, const=None, isglobal=None, isnonlocal=None):
self.top.add(name) self.top.add(name)
def scope_for(self, name): def scope_for(self, name: str) -> Optional[Dict[str, NameDescriptor]]:
# type: str -> Optional[Dict[str, NameDescriptor]]
for _, scope in reversed(self.scopes): for _, scope in reversed(self.scopes):
if name in scope: if name in scope:
return scope return scope
@ -95,6 +101,27 @@ def delete(self, name):
del scope[name] del scope[name]
def __str__(self):
out = StringIO()
out.write("-" * 78)
out.write("\n")
for scope_name, scope in self.scopes:
out.write(f"[{scope_name}]\n")
for name, desc in scope.items():
out.write(" ")
out.write(f"{name:20}")
out.write("C" if desc.const else "-")
out.write("G" if desc.isglobal else "-")
out.write("N" if desc.isnonlocal else "-")
out.write("\n")
out.write("-" * 78)
out.write("\n")
return out.getvalue()
def target_names(node, names=None): def target_names(node, names=None):
"""Get names of values targeted by assignment. """Get names of values targeted by assignment.
@ -107,17 +134,26 @@ def target_names(node, names=None):
names = set() names = set()
if isinstance(node, ast.Name): if isinstance(node, ast.Name):
print(" added", node.id)
names.add(node.id) names.add(node.id)
elif isinstance(node, (ast.List, ast.Tuple)): elif isinstance(node, (ast.List, ast.Tuple)):
print(" adding from", node.elts)
for elt in node.elts: for elt in node.elts:
names += target_names(elt) names += target_names(elt)
elif isinstance(node, (ast.Attribute, ast.Subscript, ast.Starred)): elif isinstance(node, (ast.Attribute, ast.Subscript, ast.Starred)):
print(" adding from", node.value)
names += target_names(node.value) names += target_names(node.value)
# TODO: handle the attr in an Attribute to figure out if it's a variable in a # TODO: handle the attr in an Attribute to figure out if it's a variable in a
# TODO: scope somewhere # TODO: scope somewhere
else:
print("WHAT IS THIS: ", type(node))
print(names)
return names
def constexpr(node): def constexpr(node):
if isinstance(node, ast.Constant): if isinstance(node, ast.Constant):
@ -209,7 +245,7 @@ def visit_GeneratorExp(self, node, leak=False):
# We currently only test if the entire iter expression is const. # We currently only test if the entire iter expression is const.
# #
# TODO: if we need this to handle different targets separately , e.g. x and y in: # TODO: if we need this to handle different targets separately, e.g. x and y in:
# #
# [(x, y) for x, y in [(a, 1), (b, 2), (c, 3)]] # [(x, y) for x, y in [(a, 1), (b, 2), (c, 3)]]
# #
@ -217,30 +253,31 @@ def visit_GeneratorExp(self, node, leak=False):
# So both x and y would be non-const here. # So both x and y would be non-const here.
const = constexpr(comp.iter) const = constexpr(comp.iter)
print(self.scopes)
names = target_names(comp.target)
for name in target_names(comp.target): for name in target_names(comp.target):
self.scopes. # self.scopes.
self.scopes.define(name.id, const=const) # self.scopes.define(name.id, const=const)
pass
# visit element expressions once the generator clauses are done # visit element expressions once the generator clauses are done
self.generic_visit(node.elt) self.generic_visit(node.elt)
if not leak: if not leak:
for comp in node.generators: for comp in reversed(node.generators):
self.scopes.pop("<generatorexp>") self.scopes.pop("<generatorexp>")
self.generic_visit(node) self.generic_visit(node)
def visit_ListComp(self, node): def visit_ListComp(self, node):
# use leak-True b/c we support Python 2 self.visit_GeneratorExp(node)
self.visit_GeneratorExp(node, leak=True)
def visit_SetComp(self, node): def visit_SetComp(self, node):
# use leak-True b/c we support Python 2 self.visit_GeneratorExp(node)
self.visit_GeneratorExp(node, leak=True)
def visit_DictComp(self, node): def visit_DictComp(self, node):
# use leak-True b/c we support Python 2 self.visit_GeneratorExp(node)
self.visit_GeneratorExp(node, leak=True)
def visit_Lambda(self, node): def visit_Lambda(self, node):
self.generic_visit(node) self.generic_visit(node)
@ -301,6 +338,9 @@ def visit_Expr(self, node):
# Directives are represented in the AST as named function call expressions (as # Directives are represented in the AST as named function call expressions (as
# opposed to function calls through a variable callback). # opposed to function calls through a variable callback).
if is_directive(node): if is_directive(node):
print(node.value.args[0].value)
for arg in node.value.args: for arg in node.value.args:
if not constexpr(arg): if not constexpr(arg):
# self.issues.append("ARG: %s" % ast.dump(arg)) # self.issues.append("ARG: %s" % ast.dump(arg))
@ -323,8 +363,9 @@ def visit_Expr(self, node):
root = ast.parse(source) root = ast.parse(source)
const = ConstDirectives(pkg_name) const = ConstDirectives(pkg_name)
print("PACKAGE:", pkg_name)
const.visit(root) const.visit(root)
if not const.const: if not const.const:
print("PACKAGE:", pkg_name)
for issue in const.issues: for issue in const.issues:
print(" ", issue) print(" ", issue)