1.1 --- a/annotate.py Sat Oct 07 01:50:03 2006 +0200
1.2 +++ b/annotate.py Sat Oct 07 19:46:21 2006 +0200
1.3 @@ -69,7 +69,7 @@
1.4
1.5 # Exceptions.
1.6
1.7 -class FailureError(Exception):
1.8 +class AnnotationError(Exception):
1.9 def __init__(self, exc, node, *args):
1.10 Exception.__init__(self, *args)
1.11 self.nodes = [node]
1.12 @@ -79,6 +79,9 @@
1.13 def __str__(self):
1.14 return "%s, %s" % (self.exc, self.nodes)
1.15
1.16 +class AnnotationMessage(Exception):
1.17 + pass
1.18 +
1.19 # Annotation.
1.20
1.21 class Annotator(Visitor):
1.22 @@ -146,6 +149,13 @@
1.23 mutate nodes in the original program.
1.24 """
1.25
1.26 + # Prevent infinite recursion.
1.27 +
1.28 + if node in self.current_subprograms:
1.29 + return node
1.30 +
1.31 + # Determine the namespace.
1.32 +
1.33 if locals:
1.34 self.namespace = locals
1.35 else:
1.36 @@ -219,11 +229,11 @@
1.37 def dispatch(self, node, *args):
1.38 try:
1.39 return Visitor.dispatch(self, node, *args)
1.40 - except FailureError, exc:
1.41 + except AnnotationError, exc:
1.42 exc.add(node)
1.43 raise
1.44 - except Exception, exc:
1.45 - raise FailureError(exc, node)
1.46 + except AnnotationMessage, exc:
1.47 + raise AnnotationError(exc, node)
1.48
1.49 def visitLoadRef(self, loadref):
1.50 self.namespace.set_types([Attribute(None, loadref.ref)])
1.51 @@ -327,7 +337,8 @@
1.52 invoke.expr = self.dispatch(invoke.expr)
1.53 invocation_types = self.namespace.types
1.54
1.55 - self.process_args(invoke)
1.56 + if isinstance(invoke, InvokeFunction):
1.57 + self.process_args(invoke)
1.58
1.59 # Now locate and invoke the subprogram. This can be complicated because
1.60 # the target may be a class or object, and there may be many different
1.61 @@ -403,6 +414,9 @@
1.62
1.63 return invoke
1.64
1.65 + visitInvokeFunction = visitInvoke
1.66 + visitInvokeBlock = visitInvoke
1.67 +
1.68 # Utility methods.
1.69
1.70 def invoke_subprogram(self, invoke, subprogram):
1.71 @@ -493,56 +507,69 @@
1.72 else:
1.73 args = invocation.args
1.74
1.75 + # Sort the arguments into positional and keyword arguments.
1.76 +
1.77 + pos_args = []
1.78 + kw_args = []
1.79 + add_kw = 0
1.80 + for arg in args:
1.81 + if not add_kw:
1.82 + if not isinstance(arg, Keyword):
1.83 + pos_args.append(arg)
1.84 + else:
1.85 + add_kw = 1
1.86 + if add_kw:
1.87 + if isinstance(arg, Keyword):
1.88 + kw_args.append(arg)
1.89 + else:
1.90 + raise AnnotationMessage, "Positional argument appears after keyword arguments in '%s'." % callfunc
1.91 +
1.92 params = subprogram.params
1.93 items = []
1.94 - keywords = {}
1.95 -
1.96 - # Process the specified arguments.
1.97 + star_args = []
1.98
1.99 - for arg in args:
1.100 - if isinstance(arg, Keyword):
1.101 - keywords[arg.name] = arg.expr
1.102 - continue
1.103 - elif params:
1.104 + # Match each positional argument, taking excess arguments as star args.
1.105 +
1.106 + for arg in pos_args:
1.107 + if params:
1.108 param, default = params[0]
1.109 if arg is None:
1.110 arg = default
1.111 + items.append((param, arg.types))
1.112 + params = params[1:]
1.113 else:
1.114 - raise TypeError, "Invocation of '%s' has too many arguments for %s, *%s, **%s." % (
1.115 - subprogram, subprogram.params, subprogram.star, subprogram.dstar)
1.116 - items.append((param, arg.types))
1.117 - params = params[1:]
1.118 + star_args.append(arg)
1.119
1.120 # Collect the remaining defaults.
1.121
1.122 while params:
1.123 param, default = params[0]
1.124 - if keywords.has_key(param):
1.125 - arg = keywords[param]
1.126 - else:
1.127 - arg = self.dispatch(default) # NOTE: Review reprocessing.
1.128 + if kw_args.has_key(param):
1.129 + arg = kw_args[param]
1.130 + elif default is None:
1.131 + raise AnnotationMessage, "No argument supplied in '%s' for parameter '%s'." % (subprogram, param)
1.132 items.append((param, arg.types))
1.133 params = params[1:]
1.134
1.135 # Add star and dstar.
1.136
1.137 - if invocation.star is not None:
1.138 + if star_args or invocation.star is not None:
1.139 if subprogram.star is not None:
1.140 param, default = subprogram.star
1.141 items.append((param, invocation.star.types))
1.142 else:
1.143 - raise TypeError, "Invocation provides unwanted *args."
1.144 + raise AnnotationMessage, "Invocation provides unwanted *args."
1.145 elif subprogram.star is not None:
1.146 param, default = subprogram.star
1.147 arg = self.dispatch(default) # NOTE: Review reprocessing.
1.148 items.append((param, arg.types))
1.149
1.150 - if invocation.dstar is not None:
1.151 + if kw_args or invocation.dstar is not None:
1.152 if subprogram.dstar is not None:
1.153 param, default = subprogram.dstar
1.154 items.append((param, invocation.dstar.types))
1.155 else:
1.156 - raise TypeError, "Invocation provides unwanted **args."
1.157 + raise AnnotationMessage, "Invocation provides unwanted **args."
1.158 elif subprogram.dstar is not None:
1.159 param, default = subprogram.dstar
1.160 arg = self.dispatch(default) # NOTE: Review reprocessing.