1.1 --- a/TO_DO.txt Sun Jun 13 02:24:35 2010 +0200
1.2 +++ b/TO_DO.txt Mon Jun 14 01:46:42 2010 +0200
1.3 @@ -1,3 +1,7 @@
1.4 +Consider labelling _scope on assignments and dealing with the assignment of removed
1.5 +attributes, possibly removing the entire assignment, and distinguishing between such cases
1.6 +and unknown names.
1.7 +
1.8 Check name origin where multiple branches could yield multiple scope interpretations:
1.9
1.10 ----
2.1 --- a/micropython/ast.py Sun Jun 13 02:24:35 2010 +0200
2.2 +++ b/micropython/ast.py Mon Jun 14 01:46:42 2010 +0200
2.3 @@ -126,8 +126,8 @@
2.4 block = self.new_block()
2.5 self.set_block(block)
2.6
2.7 - if self.module.module is not None:
2.8 - self.dispatch(self.module.module)
2.9 + if self.module.astnode is not None:
2.10 + self.dispatch(self.module.astnode)
2.11
2.12 # Finish off the translated program if appropriate.
2.13
2.14 @@ -417,17 +417,7 @@
2.15 def visitListCompIf(self, node): raise TranslationNotImplementedError("ListCompIf")
2.16
2.17 def visitName(self, node):
2.18 -
2.19 - # Handle names referring to constants.
2.20 -
2.21 - if self.importer.predefined_constants.has_key(node.name):
2.22 - const = self.importer.get_predefined_constant(node.name)
2.23 - self.new_op(LoadConst(const))
2.24 -
2.25 - # Handle all other names.
2.26 -
2.27 - else:
2.28 - self._visitName(node, self.name_load_instructions)
2.29 + self._visitName(node, self.name_load_instructions)
2.30
2.31 def visitSlice(self, node):
2.32 if node.lower is None:
3.1 --- a/micropython/common.py Sun Jun 13 02:24:35 2010 +0200
3.2 +++ b/micropython/common.py Mon Jun 14 01:46:42 2010 +0200
3.3 @@ -111,6 +111,51 @@
3.4
3.5 pass
3.6
3.7 +# Special representations.
3.8 +
3.9 +class AtLeast:
3.10 +
3.11 + "A special representation for numbers of a given value or greater."
3.12 +
3.13 + def __init__(self, count):
3.14 + self.count = count
3.15 +
3.16 + def __eq__(self, other):
3.17 + return 0
3.18 +
3.19 + __lt__ = __le__ = __eq__
3.20 +
3.21 + def __ne__(self, other):
3.22 + return 1
3.23 +
3.24 + def __gt__(self, other):
3.25 + if isinstance(other, AtLeast):
3.26 + return 0
3.27 + else:
3.28 + return self.count > other
3.29 +
3.30 + def __ge__(self, other):
3.31 + if isinstance(other, AtLeast):
3.32 + return 0
3.33 + else:
3.34 + return self.count >= other
3.35 +
3.36 + def __iadd__(self, other):
3.37 + if isinstance(other, AtLeast):
3.38 + self.count += other.count
3.39 + else:
3.40 + self.count += other
3.41 + return self
3.42 +
3.43 + def __radd__(self, other):
3.44 + if isinstance(other, AtLeast):
3.45 + return AtLeast(self.count + other.count)
3.46 + else:
3.47 + return AtLeast(self.count + other)
3.48 +
3.49 + def __repr__(self):
3.50 + return "AtLeast(%r)" % self.count
3.51 +
3.52 # Useful data.
3.53
3.54 comparison_methods = {
4.1 --- a/micropython/data.py Sun Jun 13 02:24:35 2010 +0200
4.2 +++ b/micropython/data.py Mon Jun 14 01:46:42 2010 +0200
4.3 @@ -53,6 +53,7 @@
4.4 """
4.5
4.6 from micropython.program import DataObject, DataValue, ReplaceableContext, PlaceholderContext
4.7 +from micropython.common import AtLeast, InspectError
4.8
4.9 def shortrepr(obj):
4.10 if obj is None:
4.11 @@ -60,51 +61,6 @@
4.12 else:
4.13 return obj.__shortrepr__()
4.14
4.15 -# Special representations.
4.16 -
4.17 -class AtLeast:
4.18 -
4.19 - "A special representation for numbers of a given value or greater."
4.20 -
4.21 - def __init__(self, count):
4.22 - self.count = count
4.23 -
4.24 - def __eq__(self, other):
4.25 - return 0
4.26 -
4.27 - __lt__ = __le__ = __eq__
4.28 -
4.29 - def __ne__(self, other):
4.30 - return 1
4.31 -
4.32 - def __gt__(self, other):
4.33 - if isinstance(other, AtLeast):
4.34 - return 0
4.35 - else:
4.36 - return self.count > other
4.37 -
4.38 - def __ge__(self, other):
4.39 - if isinstance(other, AtLeast):
4.40 - return 0
4.41 - else:
4.42 - return self.count >= other
4.43 -
4.44 - def __iadd__(self, other):
4.45 - if isinstance(other, AtLeast):
4.46 - self.count += other.count
4.47 - else:
4.48 - self.count += other
4.49 - return self
4.50 -
4.51 - def __radd__(self, other):
4.52 - if isinstance(other, AtLeast):
4.53 - return AtLeast(self.count + other.count)
4.54 - else:
4.55 - return AtLeast(self.count + other)
4.56 -
4.57 - def __repr__(self):
4.58 - return "AtLeast(%r)" % self.count
4.59 -
4.60 # Mix-ins and abstract classes.
4.61
4.62 class Naming:
4.63 @@ -122,10 +78,9 @@
4.64 "A mix-in providing dictionary methods."
4.65
4.66 def __init__(self, module=None):
4.67 + self.module = module
4.68 self.namespace = {}
4.69 self.globals = set()
4.70 - self.scope_usage = {}
4.71 - self.module = module
4.72 self.finalised = 0
4.73
4.74 # Attributes accessed on objects, potentially narrowing their types.
4.75 @@ -138,7 +93,13 @@
4.76 self.user_shelves = []
4.77 self.loop_users = [{}] # stack of loop nodes
4.78
4.79 + # Scope usage, indicating the origin of names.
4.80 +
4.81 + self.scope_usage = [{}] # stack of scope usage
4.82 + self.scope_shelves = []
4.83 +
4.84 # Define attribute usage to identify active program sections.
4.85 + # Attribute users are AST nodes defining names.
4.86
4.87 self.all_attribute_users = set()
4.88
4.89 @@ -162,6 +123,56 @@
4.90 def __getitem__(self, name):
4.91 return self.namespace[name]
4.92
4.93 + def get_using_node(self, name, node):
4.94 +
4.95 + """
4.96 + Access the given 'name' through this namespace, making use of the module
4.97 + and builtins namespaces if necessary, annotating the given 'node' with
4.98 + the scope involved.
4.99 + """
4.100 +
4.101 + attr, scope, full_name = self._get_with_scope(name)
4.102 +
4.103 + if scope is not None:
4.104 + node._scope = scope
4.105 + self.note_scope(name, scope)
4.106 +
4.107 + if full_name is not None:
4.108 + self.use_specific_attribute(full_name, name)
4.109 +
4.110 + return attr
4.111 +
4.112 + def _get_with_scope(self, name, external=0):
4.113 +
4.114 + module = self.module
4.115 + builtins = module and module.builtins or None
4.116 + importer = module and module.importer or None
4.117 +
4.118 + # Constants.
4.119 +
4.120 + if importer is not None and importer.predefined_constants.has_key(name):
4.121 + return importer.get_predefined_constant(name), "constant", None
4.122 +
4.123 + # Locals.
4.124 +
4.125 + elif not external and self.has_key(name):
4.126 + return self[name], "local", self.full_name()
4.127 +
4.128 + # Globals.
4.129 +
4.130 + elif module is not None and module.has_key(name):
4.131 + return module[name], "global", module.full_name()
4.132 +
4.133 + # Builtins.
4.134 +
4.135 + elif builtins is not None and builtins.has_key(name):
4.136 + return builtins[name], "builtins", builtins.full_name()
4.137 +
4.138 + # Unknown.
4.139 +
4.140 + else:
4.141 + return None, None, None
4.142 +
4.143 def get(self, name, default=None):
4.144 return self.namespace.get(name, default)
4.145
4.146 @@ -181,6 +192,7 @@
4.147 self.module.set(name, value, 0)
4.148 else:
4.149 self._set(name, value, single_assignment)
4.150 + self.note_scope(name, "local")
4.151
4.152 def set_module(self, name, value):
4.153
4.154 @@ -268,31 +280,11 @@
4.155
4.156 if not self.namespace.has_key(name):
4.157 self.globals.add(name)
4.158 + self.note_scope(name, "global")
4.159 return 1
4.160 else:
4.161 return 0
4.162
4.163 - def note_scope(self, name, scope):
4.164 -
4.165 - "Note usage of 'name' from the given 'scope' in the current namespace."
4.166 -
4.167 - if not self.scope_usage.has_key(name):
4.168 - self.scope_usage[name] = scope
4.169 - return 1
4.170 - elif self.scope_usage[name] == scope:
4.171 - return 1
4.172 - else:
4.173 - return 0
4.174 -
4.175 - def used_in_scope(self, name, scope):
4.176 -
4.177 - """
4.178 - Return whether 'name' is used from the given 'scope' in the current
4.179 - namespace.
4.180 - """
4.181 -
4.182 - return self.scope_usage.get(name) == scope
4.183 -
4.184 # Attribute positioning.
4.185
4.186 def attributes_as_list(self):
4.187 @@ -346,6 +338,24 @@
4.188
4.189 return self._use_attribute(name, attrname)
4.190
4.191 + def use_specific_attribute(self, objname, attrname):
4.192 +
4.193 + """
4.194 + Note attribute usage specifically on 'objname' - an object which is
4.195 + known at inspection time - or in the current unit if 'objname' is None,
4.196 + nominating a specific attribute 'attrname'.
4.197 +
4.198 + This bypasses attribute user mechanisms.
4.199 + """
4.200 +
4.201 + from_name = self.full_name()
4.202 + objname = objname or from_name
4.203 + module = self.module
4.204 + importer = module and module.importer
4.205 +
4.206 + if importer is not None:
4.207 + importer.use_specific_name(objname, attrname, from_name)
4.208 +
4.209 # These shadow various methods in the InspectedModule class, and provide
4.210 # implementations generally.
4.211
4.212 @@ -417,6 +427,8 @@
4.213
4.214 node._attrnames[name] = set()
4.215
4.216 + # Branch management methods.
4.217 +
4.218 def _new_branchpoint(self):
4.219
4.220 """
4.221 @@ -425,6 +437,7 @@
4.222 """
4.223
4.224 self.user_shelves.append([])
4.225 + self.scope_shelves.append([])
4.226
4.227 def _new_branch(self, loop_node=None):
4.228
4.229 @@ -438,24 +451,31 @@
4.230
4.231 # Retain a record of active users.
4.232
4.233 - d = {}
4.234 - d.update(self.attribute_users[-1])
4.235 - self.attribute_users.append(d)
4.236 + new_users = {}
4.237 + new_users.update(self.attribute_users[-1])
4.238 + self.attribute_users.append(new_users)
4.239
4.240 - new_users = self.attribute_users[-1]
4.241 + # Where a loop is the cause of the branch, register the loop node as a
4.242 + # user of each name so that attribute usage is also recorded for the
4.243 + # loop.
4.244
4.245 - d = {}
4.246 - d.update(self.loop_users[-1])
4.247 + loop_users = {}
4.248 + loop_users.update(self.loop_users[-1])
4.249 + self.loop_users.append(loop_users)
4.250
4.251 if loop_node is not None:
4.252 for name in new_users.keys():
4.253 - if not d.has_key(name):
4.254 - d[name] = set([loop_node])
4.255 + if not loop_users.has_key(name):
4.256 + loop_users[name] = set([loop_node])
4.257 else:
4.258 - d[name] = d[name].union([loop_node])
4.259 + loop_users[name] = loop_users[name].union([loop_node])
4.260 self._init_attribute_user_for_name(loop_node, name)
4.261
4.262 - self.loop_users.append(d)
4.263 + # Retain a record of scope usage.
4.264 +
4.265 + scope_usage = {}
4.266 + scope_usage.update(self.scope_usage[-1])
4.267 + self.scope_usage.append(scope_usage)
4.268
4.269 def _abandon_branch(self):
4.270 pass
4.271 @@ -472,6 +492,9 @@
4.272 users = self.attribute_users.pop()
4.273 self.user_shelves[-1].append(users)
4.274
4.275 + scope_usage = self.scope_usage.pop()
4.276 + self.scope_shelves[-1].append(scope_usage)
4.277 +
4.278 def _merge_branches(self):
4.279
4.280 """
4.281 @@ -513,6 +536,60 @@
4.282
4.283 self.attribute_users[-1] = new_users
4.284
4.285 + # Combine the scope usage.
4.286 +
4.287 + scope_usage = self.scope_usage[-1]
4.288 + new_scope_usage = {}
4.289 +
4.290 + all_scope_usage = self.scope_shelves.pop()
4.291 + all_scope_names = set()
4.292 +
4.293 + # Find all the names for whom scope information has been defined.
4.294 +
4.295 + for shelved_usage in all_scope_usage:
4.296 + all_scope_names.update(shelved_usage.keys())
4.297 +
4.298 + for shelved_usage in all_scope_usage:
4.299 + for name in all_scope_names:
4.300 +
4.301 + # Find the recorded scope for the name.
4.302 +
4.303 + if shelved_usage.has_key(name):
4.304 + scope = shelved_usage[name]
4.305 + elif scope_usage.has_key(name):
4.306 + scope = scope_usage[name]
4.307 +
4.308 + # If no scope is recorded, find a suitable external source.
4.309 +
4.310 + else:
4.311 + attr, scope, full_name = self._get_with_scope(name, external=1)
4.312 +
4.313 + # Attempt to record the scope, testing for conflicts.
4.314 +
4.315 + if scope:
4.316 + if not new_scope_usage.has_key(name):
4.317 + new_scope_usage[name] = scope
4.318 + elif new_scope_usage[name] != scope:
4.319 + raise InspectError("Scope conflict for %r." % name)
4.320 +
4.321 + # Scope usage methods.
4.322 +
4.323 + def note_scope(self, name, scope):
4.324 +
4.325 + "Note usage of 'name' from the given 'scope' in the current namespace."
4.326 +
4.327 + self.scope_usage[-1][name] = scope
4.328 +
4.329 + def used_in_scope(self, name, scope):
4.330 +
4.331 + """
4.332 + Return whether 'name' is used from the given 'scope' in the current
4.333 + namespace.
4.334 + """
4.335 +
4.336 + scope_usage = self.scope_usage[-1]
4.337 + return scope_usage.get(name) == scope
4.338 +
4.339 # Program data structures.
4.340
4.341 class Attr:
4.342 @@ -1409,9 +1486,10 @@
4.343
4.344 "An inspected module's core details."
4.345
4.346 - def __init__(self, name):
4.347 + def __init__(self, name, importer):
4.348 NamespaceDict.__init__(self, self)
4.349 self.name = name
4.350 + self.importer = importer
4.351 self.parent = None
4.352
4.353 # Original location details.
5.1 --- a/micropython/inspect.py Sun Jun 13 02:24:35 2010 +0200
5.2 +++ b/micropython/inspect.py Mon Jun 14 01:46:42 2010 +0200
5.3 @@ -93,13 +93,12 @@
5.4 """
5.5
5.6 ASTVisitor.__init__(self)
5.7 - Module.__init__(self, name)
5.8 + Module.__init__(self, name, importer)
5.9 self.visitor = self
5.10
5.11 # Import machinery links.
5.12
5.13 - self.importer = importer
5.14 - self.optimisations = importer.optimisations
5.15 + self.optimisations = self.importer.optimisations
5.16 self.builtins = self.importer.modules.get("__builtins__")
5.17 self.loaded = 0
5.18
5.19 @@ -114,7 +113,6 @@
5.20 self.in_function = 0 # Note function presence, affecting definitions.
5.21 self.in_loop = 0 # Note loop "membership", affecting assignments.
5.22 self.namespaces = []
5.23 - self.module = None
5.24 self.functions = []
5.25
5.26 def parse(self, filename):
5.27 @@ -128,7 +126,7 @@
5.28
5.29 "Process the given 'module'."
5.30
5.31 - self.astnode = self.module = module
5.32 + self.astnode = module
5.33
5.34 # Add __name__ to the namespace by adding an explicit assignment to the
5.35 # module.
5.36 @@ -387,20 +385,6 @@
5.37
5.38 return self.get_namespace()._use_attribute(name, attrname)
5.39
5.40 - def use_specific_attribute(self, objname, attrname):
5.41 -
5.42 - """
5.43 - Note attribute usage specifically on 'objname' - an object which is
5.44 - known at inspection time - or in the current unit if 'objname' is None,
5.45 - nominating a specific attribute 'attrname'.
5.46 -
5.47 - This bypasses attribute user mechanisms.
5.48 - """
5.49 -
5.50 - from_name = self.get_namespace().full_name()
5.51 - objname = objname or from_name
5.52 - self.importer.use_specific_name(objname, attrname, from_name)
5.53 -
5.54 # Visitor methods.
5.55
5.56 def default(self, node, *args):
5.57 @@ -933,60 +917,7 @@
5.58 visitMul = _visitBinary
5.59
5.60 def visitName(self, node):
5.61 - name = node.name
5.62 -
5.63 - # Constants.
5.64 -
5.65 - if self.importer.predefined_constants.has_key(name):
5.66 - attr = self.importer.get_predefined_constant(name)
5.67 - node._scope = "constant"
5.68 -
5.69 - # Locals.
5.70 -
5.71 - elif self.namespaces and self.namespaces[-1].has_key(name):
5.72 - attr = self.namespaces[-1][name]
5.73 -
5.74 - # Note usage of the local (potentially a class attribute).
5.75 -
5.76 - self.use_specific_attribute(None, name)
5.77 - node._scope = "local"
5.78 -
5.79 - # Globals.
5.80 -
5.81 - elif self.has_key(name):
5.82 - attr = self[name]
5.83 -
5.84 - # Note usage of the module attribute.
5.85 -
5.86 - self.use_specific_attribute(self.full_name(), name)
5.87 - node._scope = "global"
5.88 -
5.89 - # Note global usage in any local namespace.
5.90 -
5.91 - if self.namespaces:
5.92 - self.namespaces[-1].note_scope(name, "global")
5.93 -
5.94 - # Builtins.
5.95 -
5.96 - elif self.builtins is not None and self.builtins.has_key(name):
5.97 - attr = self.builtins[name]
5.98 - self.use_specific_attribute(self.builtins.full_name(), name)
5.99 - node._scope = "builtins"
5.100 -
5.101 - # Note builtins usage in any local namespace.
5.102 -
5.103 - if self.namespaces:
5.104 - self.namespaces[-1].note_scope(name, "builtins")
5.105 - else:
5.106 - self.note_scope(name, "builtins")
5.107 -
5.108 - # Unknown.
5.109 -
5.110 - else:
5.111 - attr = None
5.112 - self.use_name(name)
5.113 -
5.114 - return attr
5.115 + return self.get_namespace().get_using_node(node.name, node)
5.116
5.117 visitNot = OP
5.118
6.1 --- a/micropython/trans.py Sun Jun 13 02:24:35 2010 +0200
6.2 +++ b/micropython/trans.py Mon Jun 14 01:46:42 2010 +0200
6.3 @@ -59,12 +59,8 @@
6.4
6.5 "Return the scope for the given 'name'."
6.6
6.7 - if self.unit is not self.module and self.unit.has_key(name):
6.8 - return "local"
6.9 - elif self.module.has_key(name):
6.10 - return "global"
6.11 - else:
6.12 - return "builtins"
6.13 + attr, scope, from_name = self.unit._get_with_scope(name)
6.14 + return scope
6.15
6.16 def load_builtin(self, name, node):
6.17
6.18 @@ -1161,8 +1157,6 @@
6.19 # Get the expected scope of the name.
6.20
6.21 scope = getattr(node, "_scope", None) or self.get_scope(name)
6.22 -
6.23 - #print self.module.name, node.lineno, name, predicted_scope
6.24 self._generateName(name, scope, classes, node)
6.25
6.26 def _generateName(self, name, scope, classes, node):
6.27 @@ -1174,7 +1168,15 @@
6.28
6.29 NameInstruction, AddressInstruction, AddressContextInstruction = classes
6.30
6.31 - if scope == "local":
6.32 + # Handle names referring to constants.
6.33 +
6.34 + if scope == "constant":
6.35 + const = self.importer.get_predefined_constant(name)
6.36 + self.new_op(LoadConst(const))
6.37 +
6.38 + # Handle all other names.
6.39 +
6.40 + elif scope == "local":
6.41 unit = self.unit
6.42 if isinstance(unit, Function):
6.43 self.new_op(NameInstruction(unit.all_locals()[name]))
6.44 @@ -1200,7 +1202,8 @@
6.45 self.new_op(AddressInstruction(self.get_builtin(name, node)))
6.46
6.47 else:
6.48 - raise TranslateError("Program unit uses unknown name %r." % name)
6.49 + # NOTE: This may happen because a class attribute is optimised away.
6.50 + print "Program unit uses unknown name %r." % name
6.51
6.52 def _visitUnary(self, node):
6.53
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/tests/failure/shadow_globals_builtins_conflict.py Mon Jun 14 01:46:42 2010 +0200
7.3 @@ -0,0 +1,11 @@
7.4 +#!/usr/bin/env python
7.5 +
7.6 +x = [1, 2, 3]
7.7 +
7.8 +if x:
7.9 + y = len(x)
7.10 +else:
7.11 + def len(arg):
7.12 + return 0
7.13 +
7.14 +# vim: tabstop=4 expandtab shiftwidth=4
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/tests/failure/shadow_globals_builtins_conflict_implicit.py Mon Jun 14 01:46:42 2010 +0200
8.3 @@ -0,0 +1,11 @@
8.4 +#!/usr/bin/env python
8.5 +
8.6 +x = [1, 2, 3]
8.7 +
8.8 +if not x:
8.9 + def len(arg):
8.10 + return 0
8.11 +
8.12 +y = len(x)
8.13 +
8.14 +# vim: tabstop=4 expandtab shiftwidth=4
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/tests/failure/shadow_locals_globals_if.py Mon Jun 14 01:46:42 2010 +0200
9.3 @@ -0,0 +1,12 @@
9.4 +#!/usr/bin/env python
9.5 +
9.6 +a = 1
9.7 +
9.8 +def reassign(i):
9.9 + if i:
9.10 + a = a
9.11 + return a # name has varying origin
9.12 +
9.13 +result2_1 = reassign(3)
9.14 +
9.15 +# vim: tabstop=4 expandtab shiftwidth=4