1.1 --- a/micropython/common.py Sat Oct 26 01:55:16 2013 +0200
1.2 +++ b/micropython/common.py Sat Oct 26 20:10:34 2013 +0200
1.3 @@ -22,7 +22,7 @@
1.4 from compiler.ast import AssAttr, Getattr, Name
1.5 import compiler.ast
1.6 from micropython.basicdata import Const, Constant, TypedInstance
1.7 -from micropython.data import Attr, Class, Module
1.8 +from micropython.data import BaseAttr, Class, Module
1.9 from micropython.errors import *
1.10 from os.path import split
1.11
1.12 @@ -102,7 +102,7 @@
1.13 if isinstance(obj, (Constant, TypedInstance)):
1.14 return None, obj
1.15
1.16 - if isinstance(obj, Attr):
1.17 + if isinstance(obj, BaseAttr):
1.18 return obj, obj.get_value()
1.19
1.20 return None
1.21 @@ -150,7 +150,7 @@
1.22 # Permitting multiple expression types if they provide the
1.23 # attribute.
1.24
1.25 - if isinstance(expr, Attr):
1.26 + if isinstance(expr, BaseAttr):
1.27 exprs = expr.get_values()
1.28 else:
1.29 exprs = [expr]
2.1 --- a/micropython/data.py Sat Oct 26 01:55:16 2013 +0200
2.2 +++ b/micropython/data.py Sat Oct 26 20:10:34 2013 +0200
2.3 @@ -62,6 +62,8 @@
2.4 except NameError:
2.5 from sets import Set as set
2.6
2.7 +# Context and value manipulation.
2.8 +
2.9 def get_context_and_value(value):
2.10
2.11 "Return a context, value tuple for the given 'value'."
2.12 @@ -82,6 +84,8 @@
2.13 else:
2.14 return (value, value)
2.15
2.16 +# Namespace classes.
2.17 +
2.18 class NamespaceDict(Namespace, BranchTracking):
2.19
2.20 "A mix-in providing dictionary methods."
2.21 @@ -91,6 +95,7 @@
2.22
2.23 self.module = module
2.24 self.namespace = {}
2.25 + self._locals = {} # active while still being populated
2.26 self.globals = set()
2.27 self.lambdas = {} # only really useful for functions
2.28 self.finalised = False
2.29 @@ -194,11 +199,7 @@
2.30 # Locals.
2.31
2.32 elif not external and users.has_key(name):
2.33 - attr = Attr(None, self, name)
2.34 - for user in users[name]:
2.35 - if user._values and user._values.has_key(name):
2.36 - self._set_using_attr(attr, user._values[name])
2.37 - return attr, "local", self.full_name()
2.38 + return LocalAttr(None, self, name, nodes=users[name]), "local", self.full_name()
2.39
2.40 elif not external and self.has_key(name):
2.41 return self[name], "local", self.full_name()
2.42 @@ -261,22 +262,25 @@
2.43 See: docs/assignment.txt
2.44 """
2.45
2.46 + # For locals, direct assignments to individual name users.
2.47 +
2.48 + users = self.attribute_users[-1]
2.49 +
2.50 + if users.has_key(name):
2.51 +
2.52 + # Note that upon assignment there will probably be only one user.
2.53 +
2.54 + for user in users[name]:
2.55 + user._values = user._values or {}
2.56 + attr = user._values[name] = Attr(None, self, name)
2.57 + self._set_using_attr(attr, attr_or_value, single_assignment)
2.58 +
2.59 # Add and/or obtain the namespace entry.
2.60
2.61 if not self.namespace.has_key(name):
2.62 self.namespace[name] = Attr(None, self, name)
2.63 attr = self.namespace[name]
2.64
2.65 - # Also direct assignments to individual name users.
2.66 -
2.67 - users = self.attribute_users[-1]
2.68 -
2.69 - if users.has_key(name):
2.70 - for user in users[name]:
2.71 - user._values = user._values or {}
2.72 - userattr = user._values[name] = Attr(None, self, name)
2.73 - self._set_using_attr(userattr, attr_or_value, single_assignment)
2.74 -
2.75 # Update the attribute records.
2.76
2.77 self._set_using_attr(attr, attr_or_value, single_assignment)
2.78 @@ -284,14 +288,20 @@
2.79 def _set_using_attr(self, attr, attr_or_value, single_assignment=1):
2.80
2.81 # Handle attribute assignment as well as assignment of basic objects.
2.82 +
2.83 + context_values = self.get_contexts_and_values(attr_or_value)
2.84 + attr.update(context_values, single_assignment)
2.85 +
2.86 + def get_contexts_and_values(self, attr_or_value):
2.87 +
2.88 + "Return adapted contexts and values for the given 'attr_or_value'."
2.89 +
2.90 # Attempt to fix the context if not explicitly defined.
2.91
2.92 - if isinstance(attr_or_value, Attr):
2.93 - context_values = self.get_updated_context_values(attr_or_value.context_values)
2.94 + if isinstance(attr_or_value, BaseAttr):
2.95 + return self.get_updated_context_values(attr_or_value.get_context_values())
2.96 else:
2.97 - context_values = self.get_updated_context_values([get_context_and_value(attr_or_value)])
2.98 -
2.99 - attr.update(context_values, single_assignment)
2.100 + return self.get_updated_context_values([get_context_and_value(attr_or_value)])
2.101
2.102 def get_updated_context_values(self, context_values):
2.103
2.104 @@ -359,9 +369,9 @@
2.105
2.106 # Program data structures.
2.107
2.108 -class Attr:
2.109 +class BaseAttr:
2.110
2.111 - "An attribute entry having a context."
2.112 + "A basic attribute entry."
2.113
2.114 def __init__(self, position, parent, name, parent_type=None):
2.115
2.116 @@ -377,31 +387,21 @@
2.117 self.name = name
2.118 self.parent_type = parent_type
2.119
2.120 - # Possible values.
2.121 -
2.122 - self.context_values = set()
2.123 -
2.124 - # Number of assignments per name.
2.125 -
2.126 - self.assignments = None
2.127 -
2.128 # Number of static "class" or "def" assignments per name.
2.129
2.130 self.static_assignments = 0
2.131
2.132 - # Value-related methods.
2.133 -
2.134 def get_contexts(self):
2.135 - return [c for (c, v) in self.context_values]
2.136 + return [c for (c, v) in self.get_context_values()]
2.137
2.138 def get_values(self):
2.139 - return [v for (c, v) in self.context_values]
2.140 + return [v for (c, v) in self.get_context_values()]
2.141
2.142 def get_context(self):
2.143
2.144 "Get the context referenced by the attribute."
2.145
2.146 - if self.assignments == 1 and len(self.context_values) == 1:
2.147 + if self.get_assignments() == 1 and len(self.get_context_values()) == 1:
2.148 return self.get_contexts()[0]
2.149 else:
2.150 return None
2.151 @@ -410,43 +410,13 @@
2.152
2.153 "Get the value referenced by the attribute."
2.154
2.155 - if self.assignments == 1 and len(self.context_values) == 1:
2.156 + if self.get_assignments() == 1 and len(self.get_context_values()) == 1:
2.157 return self.get_values()[0]
2.158 else:
2.159 return None
2.160
2.161 __call__ = get_value # convenient access to any single value
2.162
2.163 - def update(self, context_values, single_assignment):
2.164 -
2.165 - """
2.166 - Update the attribute, adding the 'context_values' provided to the
2.167 - known details associated with the attribute, changing the number of
2.168 - assignments according to the 'single_assignment' status of the
2.169 - operation, where a true value indicates that only one assignment is
2.170 - associated with the update, and a false value indicates that potentially
2.171 - many assignments may be involved.
2.172 - """
2.173 -
2.174 - if self.context_values.issuperset(context_values) and \
2.175 - not (make_instance(), make_instance()) in context_values:
2.176 - return
2.177 -
2.178 - self.update_assignments(len(set(context_values)), single_assignment)
2.179 - self.context_values.update(context_values)
2.180 -
2.181 - def update_assignments(self, n, single_assignment):
2.182 - if self.assignments is None:
2.183 - if single_assignment:
2.184 - self.assignments = n
2.185 - else:
2.186 - self.assignments = AtLeast(n)
2.187 - else:
2.188 - if single_assignment:
2.189 - self.assignments += n
2.190 - else:
2.191 - self.assignments += AtLeast(n)
2.192 -
2.193 def is_constant(self):
2.194
2.195 """
2.196 @@ -454,7 +424,7 @@
2.197 as being constant within a particular scope.
2.198 """
2.199
2.200 - return self.assignments == 1
2.201 + return self.get_assignments() == 1
2.202
2.203 def is_strict_constant(self):
2.204
2.205 @@ -479,7 +449,7 @@
2.206
2.207 "Return whether this attribute defines more than one class."
2.208
2.209 - if self.assignments > 1:
2.210 + if self.get_assignments() > 1:
2.211 have_class = False
2.212 for obj in self.get_values():
2.213 if isinstance(obj, Class):
2.214 @@ -562,17 +532,99 @@
2.215 parent_type = ""
2.216 return "<attribute %s.%s (%s%sassigned %r)>" % (
2.217 shortrepr(self.parent), self.name,
2.218 - parent_type, position, self.assignments
2.219 + parent_type, position, self.get_assignments()
2.220 )
2.221
2.222 def __shortrepr__(self):
2.223 return "%s.%s (at %r)" % (shortrepr(self.parent), self.name, self.position)
2.224
2.225 - def _context_values_str(self):
2.226 - l = []
2.227 - for (c, v) in self.context_values:
2.228 - l.append("(c=%s, v=%s)" % (shortrepr(c), shortrepr(v)))
2.229 - return ", ".join(l)
2.230 +class LocalAttr(BaseAttr):
2.231 +
2.232 + """
2.233 + An attribute of a local namespace - a local name - derived from potentially
2.234 + many assignments. This attribute dynamically generates value information
2.235 + from that stored on assignment nodes so that it may be possible to update
2.236 + such nodes and thus the knowledge about the nature of the attribute at a
2.237 + later point.
2.238 + """
2.239 +
2.240 + def __init__(self, position, parent, name, nodes):
2.241 + BaseAttr.__init__(self, position, parent, name)
2.242 + self.nodes = nodes or set()
2.243 +
2.244 + def get_assignments(self):
2.245 + # NOTE: Needs to consider loops.
2.246 + return len(self.nodes)
2.247 +
2.248 + def get_context_values(self):
2.249 + context_values = set()
2.250 + for node in self.nodes:
2.251 + if node._values and node._values.has_key(self.name):
2.252 + attr = node._values[self.name]
2.253 + if isinstance(attr, BaseAttr):
2.254 + context_values.update(attr.get_context_values())
2.255 + else:
2.256 + context_values.add(get_context_and_value(attr))
2.257 + return context_values
2.258 +
2.259 +class Attr(BaseAttr):
2.260 +
2.261 + "An attribute entry having context and value information."
2.262 +
2.263 + def __init__(self, position, parent, name, parent_type=None):
2.264 + BaseAttr.__init__(self, position, parent, name, parent_type)
2.265 +
2.266 + # Possible values.
2.267 +
2.268 + self.context_values = set()
2.269 +
2.270 + # Number of assignments per name.
2.271 +
2.272 + self.assignments = None
2.273 +
2.274 + def get_assignments(self):
2.275 + return self.assignments
2.276 +
2.277 + def get_context_values(self):
2.278 + return self.context_values
2.279 +
2.280 + # Generic update operations on attributes.
2.281 +
2.282 + def update(self, context_values, single_assignment):
2.283 +
2.284 + """
2.285 + Update the attribute, adding the 'context_values' provided to the
2.286 + known details associated with the attribute, changing the number of
2.287 + assignments according to the 'single_assignment' status of the
2.288 + operation, where a true value indicates that only one assignment is
2.289 + associated with the update, and a false value indicates that potentially
2.290 + many assignments may be involved.
2.291 + """
2.292 +
2.293 + if self.context_values.issuperset(context_values) and \
2.294 + not (make_instance(), make_instance()) in context_values:
2.295 + return
2.296 +
2.297 + self.update_assignments(len(set(context_values)), single_assignment)
2.298 + self.context_values.update(context_values)
2.299 +
2.300 + def update_assignments(self, n, single_assignment):
2.301 +
2.302 + """
2.303 + Update the assignment count by 'n' or "at least" 'n' if
2.304 + 'single_assignment' is given as a false value.
2.305 + """
2.306 +
2.307 + if self.assignments is None:
2.308 + if single_assignment:
2.309 + self.assignments = n
2.310 + else:
2.311 + self.assignments = AtLeast(n)
2.312 + else:
2.313 + if single_assignment:
2.314 + self.assignments += n
2.315 + else:
2.316 + self.assignments += AtLeast(n)
2.317
2.318 class Class(NamespaceDict, Naming, Constant):
2.319
3.1 --- a/micropython/deduce.py Sat Oct 26 01:55:16 2013 +0200
3.2 +++ b/micropython/deduce.py Sat Oct 26 20:10:34 2013 +0200
3.3 @@ -107,7 +107,7 @@
3.4
3.5 target = node._expr
3.6 instance_target = isinstance(target, TypedInstance)
3.7 - typed_instance_attr = isinstance(target, Attr) and isinstance(target.get_value(), TypedInstance)
3.8 + typed_instance_attr = isinstance(target, BaseAttr) and isinstance(target.get_value(), TypedInstance)
3.9 self_access = self.provides_self_access(node, unit)
3.10
3.11 # Attempt to deduce attributes from explicit annotations.
3.12 @@ -141,7 +141,7 @@
3.13
3.14 if target and (
3.15 isinstance(target, (Class, Module)) or
3.16 - isinstance(target, Attr) and not [v for v in target.get_values() if not isinstance(v, (Class, Module))]
3.17 + isinstance(target, BaseAttr) and not [v for v in target.get_values() if not isinstance(v, (Class, Module))]
3.18 ):
3.19 node._access_type = "impossible"
3.20 return
3.21 @@ -327,7 +327,7 @@
3.22
3.23 def visitAssName(self, node):
3.24 if self.expr:
3.25 - if isinstance(self.expr, Attr):
3.26 + if isinstance(self.expr, BaseAttr):
3.27 expr = self.expr.get_value()
3.28 elif isinstance(self.expr, TypedInstance):
3.29 expr = self.expr
3.30 @@ -336,12 +336,18 @@
3.31 else:
3.32 return
3.33
3.34 - value = node._values and node._values.get(node.name) or None
3.35 + attr = node._values and node._values.get(node.name) or None
3.36
3.37 # Need to replace any uncertain value with a concrete value.
3.38
3.39 - if value and isinstance(value, Instance) and not isinstance(value, TypedInstance):
3.40 - node._values[node.name] = expr
3.41 + if attr:
3.42 + if isinstance(attr, BaseAttr):
3.43 + value = attr.get_value()
3.44 + else:
3.45 + value = attr
3.46 +
3.47 + if value and isinstance(value, Instance) and not isinstance(value, TypedInstance):
3.48 + node._values[node.name] = self.expr
3.49
3.50 def visitCallFunc(self, node):
3.51
4.1 --- a/micropython/inspect.py Sat Oct 26 01:55:16 2013 +0200
4.2 +++ b/micropython/inspect.py Sat Oct 26 20:10:34 2013 +0200
4.3 @@ -676,7 +676,7 @@
4.4
4.5 # Attempt to identify the nature of the attribute.
4.6
4.7 - if isinstance(expr, Attr):
4.8 + if isinstance(expr, BaseAttr):
4.9 value = expr.get_value()
4.10
4.11 # Get the attribute and record its usage.
4.12 @@ -861,7 +861,7 @@
4.13
4.14 # Record the attribute on the presumed target.
4.15
4.16 - if isinstance(expr, Attr):
4.17 + if isinstance(expr, BaseAttr):
4.18 value = expr.get_value()
4.19
4.20 if expr.name == "self":
4.21 @@ -927,7 +927,7 @@
4.22 # NOTE: this is merely creating aliases for such methods.
4.23
4.24 if isinstance(self.get_namespace(), (Class, Module)):
4.25 - if not isinstance(self.expr, Attr) or not isinstance(self.expr.get_value(), Function):
4.26 + if not isinstance(self.expr, BaseAttr) or not isinstance(self.expr.get_value(), Function):
4.27 self.use_specific_attribute(None, node.name)
4.28 else:
4.29 fn = self.expr.get_value()
4.30 @@ -1009,7 +1009,7 @@
4.31
4.32 # Each base class must be constant and known at compile-time.
4.33
4.34 - if isinstance(expr, Attr):
4.35 + if isinstance(expr, BaseAttr):
4.36 if expr.assignments != 1:
4.37 raise InspectError("Base class %r for %r is not constant: %r" % (base, cls.full_name(), expr))
4.38 elif not isinstance(expr.get_value(), Class):
4.39 @@ -1033,8 +1033,8 @@
4.40 # Make an entry for the class in the parent namespace.
4.41
4.42 self.namespaces.pop()
4.43 + self.define_attribute_user(node)
4.44 self.store(node.name, cls, static_def=True)
4.45 - self.define_attribute_user(node)
4.46 self.add_object(cls)
4.47
4.48 # Process the class body in its own namespace.
5.1 --- a/micropython/objectset.py Sat Oct 26 01:55:16 2013 +0200
5.2 +++ b/micropython/objectset.py Sat Oct 26 20:10:34 2013 +0200
5.3 @@ -3,7 +3,7 @@
5.4 """
5.5 Object set support.
5.6
5.7 -Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie <paul@boddie.org.uk>
5.8 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Paul Boddie <paul@boddie.org.uk>
5.9
5.10 This program is free software; you can redistribute it and/or modify it under
5.11 the terms of the GNU General Public License as published by the Free Software
5.12 @@ -55,6 +55,9 @@
5.13 out.append("}")
5.14 return "".join(out)
5.15
5.16 + def __len__(self):
5.17 + return len(self.keys())
5.18 +
5.19 def __iter__(self):
5.20 return iter(self.keys())
5.21