fix cpp generation for 'import .. as'
[shedskin:mainline.git] / shared.py
1 from compiler import *
2 from compiler.ast import *
3 from compiler.visitor import *
4
5 import os, sys, traceback
6
7 from backward import *
8
9 # --- global variables gx, mv
10
11 class globalInfo: # XXX add comments, split up
12     def __init__(self):
13         self.constraints = set()
14         self.allvars = set()
15         self.allfuncs = set()
16         self.allclasses = set()
17         self.cnode = {}
18         self.types = {}
19         self.templates = 0
20         self.modules = {}
21         self.inheritance_relations = {}
22         self.inheritance_tempvars = {}
23         self.parent_nodes = {}
24         self.inherited = set()
25         self.nrcltypes = 8;
26         self.empty_constructors = set()
27         self.typeclass = {}
28         self.sig_nr = {}
29         self.nameclasses = {}
30         self.tuple2 = set()             # binary typed tuples 
31         self.module = None
32         self.simple_builtins = ['none', 'str_', 'float_', 'int_', 'class_']   
33         self.builtins = self.simple_builtins + ['list', 'tuple', 'tuple2', 'dict', 'frozenset', 'set']
34         self.assign_target = {}              # instance node for instance variable assignment
35         self.alloc_info = {}                 # allocation site type information across iterations
36         self.iterations = 0
37         self.sysdir = os.environ.get('SHEDSKIN_ROOT', '.').replace('\\','/')
38         self.libdir = connect_paths(self.sysdir, 'lib')
39         self.main_mod = 'test'
40         self.cpp_keywords = set(['asm', 'auto', 'bool', 'case', 'catch', 'char', 'const', 'const_cast', 'default', 'delete', 'do', 'double', 'dynamic_cast', 'enum', 'explicit', 'export', 'extern', 'false', 'float', 'friend', 'goto', 'inline', 'int', 'long', 'mutable', 'namespace', 'new', 'operator', 'private', 'protected', 'public', 'register', 'reinterpret_cast', 'short', 'signed', 'register', 'sizeof', 'static', 'static_cast', 'struct', 'switch', 'template', 'this', 'throw', 'true', 'typedef', 'typeid', 'typename', 'union', 'unsigned', 'using', 'virtual', 'void', 'volatile', 'wchar_t'])
41         self.cpp_keywords.update(['stdin', 'stdout', 'stderr', 'std', 'abstract', 'st_mtime', 'st_atime', 'st_ctime', 'errno', 'fileno', 'environ']) # XXX
42         self.cpp_keywords.update(['ST_ATIME', 'ST_CTIME', 'ST_DEV', 'ST_GID', 'ST_INO', 'ST_MODE', 'ST_MTIME', 'ST_NLINK', 'ST_SIZE', 'ST_UID', 'S_ENFMT', 'S_IEXEC', 'S_IFBLK', 'S_IFCHR', 'S_IFDIR', 'S_IFIFO', 'S_IFLNK', 'S_IFREG', 'S_IFSOCK', 'S_IREAD', 'S_IRGRP', 'S_IROTH', 'S_IRUSR', 'S_IRWXG', 'S_IRWXO', 'S_IRWXU', 'S_ISGID', 'S_ISUID', 'S_ISVTX', 'S_IWGRP', 'S_IWOTH', 'S_IWRITE', 'S_IWUSR', 'S_IXGRP', 'S_IXOTH', 'S_IXUSR', 'S_IMODE', 'S_IFMT', 'S_ISDIR', 'S_ISCHR', 'S_ISBLK', 'S_ISREG', 'S_ISFIFO', 'S_ISLNK', 'S_ISSOCK'])
43         self.cpp_keywords.update(['AF_INET', 'AF_UNIX', 'SOCK_STREAM', 'SOCK_DGRAM', 'SOL_IP', 'SOL_SOCKET', 'IP_TOS', 'IP_TTL', 'SHUT_RD', 'SHUT_WR', 'SHUT_RDWR', 'INADDR_ANY', 'INADDR_LOOPBACK', 'INADDR_NONE', 'INADDR_BROADCAST', 'SO_REUSEADDR', 'SOMAXCONN', 'htonl', 'htons', 'ntohl', 'ntohs'])
44         self.cpp_keywords.update(['main'])
45         self.cpp_keywords.update(['sun'])
46         self.ss_prefix = '__ss_'
47         self.list_types = {}
48         self.classes_with_init = set()
49         self.loopstack = [] # track nested loops
50         self.comments = {}
51         self.wrap_around_check = True
52         self.bounds_checking = False
53         self.extension_module = False
54         self.flags = None
55         self.method_refs = set()
56         self.avoid_loops = False
57         self.annotation = True
58         self.assignments = []
59         self.output_dir=''
60
61 def newgx():
62     return globalInfo()
63
64 def getgx():
65     return _gx
66
67 def setgx(gx):
68     global _gx
69     _gx = gx
70     return _gx
71
72 def getmv():
73     return _mv
74
75 def setmv(mv):
76     global _mv
77     _mv = mv
78     return _mv
79
80 # --- python variable, function, class, module..
81
82 class variable:
83     def __init__(self, name, parent):
84         self.name = name
85         self.parent = parent
86         self.invisible = False            # not in C++ output
87         self.formal_arg = False
88         self.template_variable = False    
89         self.template_disabled = False
90         self.imported = False
91         self.initexpr = None
92         self.filter = set()                # filters: x.append(..) means that x can only be of a class that has 'append'
93         self.registered = False
94
95     def types(self):
96         return inode(self).types()
97
98     def __repr__(self):
99         if self.parent: return repr((self.parent, self.name))
100         return self.name
101
102 class function:
103     def __init__(self, node=None, parent=None, inherited_from=None):
104         self.node = node
105         if node:
106             ident = node.name
107             if inherited_from and ident in parent.funcs:
108                 ident += inherited_from.ident+'__' # XXX ugly
109             self.ident = ident
110             self.formals = node.argnames
111             self.flags = node.flags
112             self.doc = node.doc
113         self.returnexpr = []
114         self.retnode = None
115         self.parent = parent 
116         self.constraints = set()
117         self.vars = {}
118         self.template_vars = {}
119         self.varargs = None
120         self.kwargs = None
121         self.globals = []
122         self.mv = getmv()
123         self.lnodes = []
124         self.nodes = set()
125         self.defaults = []
126         self.misses = set()
127         self.cp = {}
128         self.listcomp = False
129         self.isGenerator = False
130         self.yieldNodes = []
131         self.tvars = set()
132         self.ftypes = []                # function is called via a virtual call: arguments may have to be cast
133         self.inherited = None
134
135         if node and getmv().module.ident != 'builtin':
136             getgx().allfuncs.add(self)
137
138         self.parent_constr = None
139         self.retvars = []
140         self.invisible = False
141         self.fakeret = None
142         self.declared = False
143
144         self.registered = []
145         self.registered_tempvars = []
146
147     def __repr__(self):
148         if self.parent: return 'function '+repr((self.parent, self.ident))
149         return 'function '+self.ident
150
151 class class_:
152     def __init__(self, node):
153         self.node = node
154         self.ident = node.name
155         self.bases = []
156         self.children = []
157         self.dcpa = 1
158         self.mv = getmv()
159         self.vars = {}
160         self.funcs = {}
161         self.virtuals = {}              # 'virtually' called methods 
162         self.virtualvars = {}           # 'virtual' variables
163         self.template_vars = {}
164         self.properties = {}
165         self.staticmethods = []
166
167         self.typenr = getgx().nrcltypes
168         getgx().nrcltypes += 1
169         getgx().typeclass[self.typenr] = self
170
171         # data adaptive analysis
172         self.nrcart = {}                # nr: cart
173         self.cartnr = {}                # cart: nr
174         self.splits = {}                # contour: old contour (used between iterations)
175         self.unused = []                # unused contours
176
177         self.has_init = self.has_copy = self.has_deepcopy = False
178
179     def ancestors(self): # XXX attribute (faster)
180         a = set(self.bases)
181         changed = 1
182         while changed:
183             changed = 0
184             for cl in a.copy():
185                 if set(cl.bases)-a:
186                     changed = 1
187                     a.update(cl.bases)
188         return a
189
190     def ancestors_upto(self, other):
191         a = self
192         result = []
193         while a != other:
194             result.append(a)
195             if not a.bases:
196                 break
197             a = a.bases[0] 
198         return result
199
200     def descendants(self, inclusive=False): # XXX attribute (faster)
201         a = set()
202         if inclusive: 
203             a.add(self)
204         for cl in self.children:
205             a.add(cl)
206             a.update(cl.descendants())
207         return a
208
209     def __repr__(self):
210         return 'class '+self.ident
211
212     def template(self): # XXX nokeywords
213         if self.template_vars: return self.ident+'<'+','.join(self.template_vars)+'>'
214         else: return self.ident
215
216 class static_class: # XXX merge with regular class
217     def __init__(self, cl):
218         self.vars = {}
219         self.funcs = {}
220         self.class_ = cl
221         cl.static_class = self
222         self.ident = cl.ident
223         self.bases = []
224         self.parent = None
225         self.mv = getmv()
226         self.module = cl.module
227
228     def __repr__(self):
229         return 'static class '+self.class_.ident
230
231 class module:
232     def __init__(self, ident, node):
233         self.ident = ident
234         self.node = node
235         self.prop_includes = set()
236
237     def __repr__(self):
238         return 'module '+self.ident 
239
240 # --- constraint graph node
241
242 class cnode:
243     def __init__(self, thing, dcpa=0, cpa=0, parent=None):
244         self.thing = thing
245         self.dcpa = dcpa
246         self.cpa = cpa
247         self.fakenodes = []
248         self.fakefunc = None
249         self.parent = parent
250         self.defnodes = False # if callnode, notification nodes were made for default arguments
251         self.mv = getmv()
252         self.constructor = False # allocation site 
253         self.copymetoo = False
254         self.filters = [] # run-time type filters such as isinstance()
255         #self.parent_callfunc = None
256         self.fakert = False
257      
258         if isinstance(self.thing, CallFunc) and isinstance(self.thing.node, Name) and self.thing.node.name == 'set': 
259             if (self.thing, self.dcpa, self.cpa) in getgx().cnode:
260                 print 'killing something!', self
261                 traceback.print_stack() 
262
263         getgx().cnode[self.thing, self.dcpa, self.cpa] = self
264
265         # --- in, outgoing constraints
266
267         self.in_ = set()        # incoming nodes
268         self.out = set()        # outgoing nodes
269         self.fout = set()       # unreal outgoing edges, used in ifa
270           
271         # --- iterative dataflow analysis
272
273         self.in_list = 0        # node in work-list
274         self.callfuncs = []    # callfuncs to which node is object/argument
275         self.copybyvalue = {}   # node is copy-by-value argument (int/str/float type->number)
276
277         if isinstance(thing, CallFunc): self.changed = 1 # object/arguments have changed
278
279         self.nodecp = set()        # already analyzed cp's # XXX kill! kill!
280
281         # --- add node to surrounding non-listcomp function
282         if parent: # do this only once! (not when copying)
283             while parent and isinstance(parent, function) and parent.listcomp: parent = parent.parent 
284             if isinstance(parent, function):
285                 if self not in parent.nodes:
286                     parent.nodes.add(self)
287                     #parent.lnodes.append(self)
288
289     def copy(self, dcpa, cpa, worklist=None): # XXX to infer.py
290         #if not self.mv.module.builtin: print 'copy', self
291
292         if (self.thing, dcpa, cpa) in getgx().cnode:
293             return getgx().cnode[self.thing, dcpa, cpa]
294
295         newnode = cnode(self.thing, dcpa, cpa)
296
297         newnode.callfuncs = self.callfuncs[:] # XXX no copy?
298         newnode.constructor = self.constructor
299         newnode.copymetoo = self.copymetoo
300         newnode.parent = self.parent
301         newnode.mv = self.mv
302
303         addtoworklist(worklist, newnode)
304
305         if self.constructor or self.copymetoo or isinstance(self.thing, (Not, Compare)): # XXX XXX
306             getgx().types[newnode] = getgx().types[self].copy()
307         else:
308             getgx().types[newnode] = set()
309         return newnode
310
311     def types(self):
312         return getgx().types[self]
313
314     def __repr__(self):
315         return repr((self.thing, self.dcpa, self.cpa))
316
317 def addtoworklist(worklist, node): # XXX to infer.py
318     if worklist != None and not node.in_list:
319         worklist.append(node)
320         node.in_list = 1
321
322 def in_out(a, b):
323     a.out.add(b)
324     b.in_.add(a)
325
326 def addconstraint(a, b, worklist=None):
327     getgx().constraints.add((a,b))
328     in_out(a, b)
329     addtoworklist(worklist, a)
330     
331 # --- shortcuts
332
333 def inode(node):
334     return getgx().cnode[node,0,0]
335
336 def connect_paths(a, b, conn='/'):
337     if a == '':
338         return b
339     return a+conn+b
340
341 def relative_path(a, b):
342     c = b[len(a):]
343     if c.startswith('/'): c = c[1:]
344     return c
345
346 def is_method(parent):
347     return isinstance(parent, function) and isinstance(parent.parent, class_)
348
349 def is_listcomp(parent):
350     return isinstance(parent, function) and parent.listcomp
351
352 def fastfor(node):
353     return isinstance(node.list, CallFunc) and isinstance(node.list.node, Name) and node.list.node.name in ['range', 'xrange']
354
355 def lookupvar(name, parent):
356     return defvar(name, parent, False)
357
358 def defaultvar(name, parent, worklist=None, template_var=False):
359     var = defvar(name, parent, True, worklist, template_var)
360
361     if isinstance(parent, function) and parent.listcomp and not var.registered:
362         while isinstance(parent, function) and parent.listcomp: # XXX
363             parent = parent.parent
364         if isinstance(parent, function):
365             register_tempvar(var, parent)
366
367     return var
368
369 def defvar(name, parent, local, worklist=None, template_var=False):
370     if parent and name in parent.vars:
371         return parent.vars[name]
372     if template_var:
373         dest = parent.template_vars
374     elif parent and local:
375         dest = parent.vars
376     else:
377         # recursive lookup
378         chain = []
379         while isinstance(parent, function):
380             if name in parent.vars:
381                 for ancestor in chain:
382                     if isinstance(ancestor, function): # XXX optimize
383                         ancestor.misses.add(name)
384                 return parent.vars[name]
385             chain.append(parent)
386             parent = parent.parent
387             
388         # not found: global
389         if name in getmv().globals:
390             return getmv().globals[name]
391         dest = getmv().globals
392
393     if not local:
394         return None
395
396     var = variable(name, parent)
397     if template_var:
398         var.template_variable = True
399     else:
400         getgx().allvars.add(var)
401
402     dest[name] = var
403     newnode = cnode(var, parent=parent) 
404     if parent:
405         newnode.mv = parent.mv
406     addtoworklist(worklist, newnode)
407     getgx().types[newnode] = set()
408
409     return var
410
411 def defclass(name):
412     if name in getmv().classes: return getmv().classes[name]
413     else: return getmv().ext_classes[name]
414
415 def deffunc(name):
416     if name in getmv().funcs: return getmv().funcs[name]
417     else: return getmv().ext_funcs[name]
418
419 class fakeGetattr(Getattr): pass # XXX ugly
420 class fakeGetattr2(Getattr): pass
421 class fakeGetattr3(Getattr): pass
422
423 def lookupmodule(node, imports):
424     path = []
425
426     while isinstance(node, Getattr):
427         path = [node.attrname] + path
428         node = node.expr
429
430     if isinstance(node, Name):
431         path = [node.name] + path
432
433         # --- search import chain
434         for ident in path:
435             if ident in imports:
436                 mod = imports[ident]
437                 imports = mod.mv.imports
438             else:
439                 return None
440         
441         return mod
442
443 def lookupclass(node, imports):
444     if isinstance(node, Name):
445         if node.name in getmv().classes: return getmv().classes[node.name]
446         elif node.name in getmv().ext_classes: return getmv().ext_classes[node.name]
447         else: return None
448
449     elif isinstance(node, Getattr):
450         module = lookupmodule(node.expr, imports)
451         if module and node.attrname in module.classes:
452             return module.classes[node.attrname]
453
454     return None
455
456 # --- recursively determine (lvalue, rvalue) pairs in assignment expressions
457
458 def assign_rec(left, right):
459     # determine lvalues and rvalues
460     if isinstance(left, (AssTuple, AssList)): 
461         lvalues = left.getChildNodes()
462     else: 
463         lvalues = [left]
464
465     if len(lvalues) > 1:
466         if isinstance(right, (Tuple, List)): 
467             rvalues = right.getChildNodes()
468         else:
469             return [(left, right)]
470     else:
471         rvalues = [right]
472
473     # pair corresponding arguments
474     pairs = []
475     for (lvalue,rvalue) in zip(lvalues, rvalues):
476          if isinstance(lvalue, (AssTuple, AssList)): 
477              pairs += assign_rec(lvalue, rvalue)
478          else:
479              pairs.append((lvalue, rvalue))
480
481     return pairs
482
483 def get_ident(node):
484     if not isinstance(node, Const) or not isinstance(node.value, int):
485         return '__getitem__'
486
487     if node.value == 0:  return '__getfirst__'
488     elif node.value == 1: return '__getsecond__'
489     return '__getitem__'
490
491 def augmsg(node, msg):
492     if hasattr(node, 'augment'): return '__i'+msg+'__'
493     return '__'+msg+'__'
494
495 errormsgs = set()
496
497 def error(msg, node=None, warning=False):
498     if msg in errormsgs: return
499     errormsgs.add(msg)
500         
501     if warning: type = '*WARNING*'
502     else: type = '*ERROR*'
503
504     if node: lineno = ':'+str(node.lineno)
505     else: lineno = ''
506
507     msg = type+' '+getmv().module.filename+lineno+': '+msg
508     print msg
509
510     if not warning:
511         sys.exit()
512
513 # --- merge constraint network along combination of given dimensions (dcpa, cpa, inheritance)
514 # e.g. for annotation we merge everything; for code generation, we might want to create specialized code
515 def merged(nodes, dcpa=False, inheritance=False): 
516     merge = {}
517
518     #for n in nodes:
519     #    if isinstance(n.thing, Name) and n.thing.name == '__0':
520     #        print 'jow', n, n.parent, n.thing in getgx().inherited
521
522     if inheritance: # XXX do we really need this crap
523         mergeinh = merged([n for n in nodes if n.thing in getgx().inherited])
524         mergenoinh = merged([n for n in nodes if not n.thing in getgx().inherited]) 
525
526     for node in nodes:
527         # --- merge node types
528         if dcpa: sort = (node.thing, node.dcpa)
529         else: sort = node.thing
530         merge.setdefault(sort, set()).update(getgx().types[node]) 
531
532         # --- merge inheritance nodes
533         if inheritance:
534             inh = getgx().inheritance_relations.get(node.thing, [])
535
536             # merge function variables with their inherited versions (we don't customize!)
537             if isinstance(node.thing, variable) and isinstance(node.thing.parent, function):
538                 var = node.thing
539
540                 for inhfunc in getgx().inheritance_relations.get(var.parent, []):
541
542                     if var.name in inhfunc.vars:
543                         if inhfunc.vars[var.name] in mergenoinh: 
544                             merge.setdefault(sort, set()).update(mergenoinh[inhfunc.vars[var.name]])
545
546                 for inhvar in getgx().inheritance_tempvars.get(var, []): # XXX more general
547                     if inhvar in mergenoinh: 
548                         merge.setdefault(sort, set()).update(mergenoinh[inhvar])
549
550             # node is not a function variable
551             else:
552                 for n in inh:
553                     if n in mergeinh: # XXX ook mergenoinh?
554                         merge.setdefault(sort, set()).update(mergeinh[n]) 
555
556     return merge
557
558 # --- analyze call expression: namespace, method call, direct call/constructor..
559
560 def analyze_callfunc(node, check_exist=False): # XXX generate target list XXX uniform variable system!
561     #print 'analyze callnode', node, inode(node).parent
562     namespace, objexpr, method_call, mod_var, parent_constr = inode(node).mv.module, None, False, False, False # XXX mod_var
563     constructor, direct_call = None, None
564     imports = inode(node).mv.imports
565
566     # method call
567     if isinstance(node.node, Getattr): 
568         objexpr, ident = node.node.expr, node.node.attrname
569
570         # parent constr
571         if isinstance(objexpr, Name) and inode(node).parent: # XXX Name
572             cl = inode(node).parent.parent
573             if isinstance(cl, class_) and objexpr.name in [x.ident for x in cl.bases]:
574                 parent_constr = True
575                 ident = ident+objexpr.name+'__'
576                 return objexpr, ident, direct_call, method_call, constructor, mod_var, parent_constr
577
578         # XXX a.b.c.. : a local variable!
579
580         # staticmethod
581         cl = lookupclass(node.node.expr, imports)
582         if cl and ident in cl.staticmethods:  
583             direct_call = cl.funcs[ident]
584             return objexpr, ident, direct_call, method_call, constructor, mod_var, parent_constr
585
586         module = lookupmodule(node.node.expr, imports)
587         if module: 
588             namespace, objexpr = module, None
589         else:
590             method_call = True
591
592     elif isinstance(node.node, Name):
593         ident = node.node.name
594     else:
595         ident = 'meuk' # XXX ?
596
597     # direct [constructor] call
598     if isinstance(node.node, Name) or namespace != inode(node).mv.module: 
599         if ident in ['max','min','sum'] and len(node.args) == 1:
600             ident = '__'+ident
601         elif ident == 'zip' and len(node.args) <= 3:
602             if not node.args:
603                 error("please provide 'zip' with arguments", node)
604             ident = '__zip'+str(len(node.args))
605
606         if ident in ['list','tuple','frozenset','set','dict'] and not node.args:
607             constructor = namespace.mv.ext_classes[ident]
608         elif ident in namespace.mv.classes:
609             constructor = namespace.mv.classes[ident]
610         elif ident not in ['list','tuple','dict'] and ident in namespace.mv.ext_classes: # XXX cleanup
611             constructor = namespace.mv.ext_classes[ident]
612         elif ident in namespace.mv.funcs:
613             direct_call = namespace.mv.funcs[ident]
614         elif ident in namespace.mv.ext_funcs:
615             direct_call = namespace.mv.ext_funcs[ident]
616
617         else:
618             if isinstance(node.node, Name):
619                 var = lookupvar(ident, inode(node).parent)
620                 if var:
621                     return objexpr, ident, direct_call, method_call, constructor, mod_var, parent_constr
622
623             if namespace != inode(node).mv.module:
624                 return objexpr, ident, None, False, None, True, False
625             elif check_exist: 
626                 traceback.print_stack()
627                 error("unbound identifier '"+ident+"'", node)
628
629     return objexpr, ident, direct_call, method_call, constructor, mod_var, parent_constr
630
631 # --- return list of potential call targets
632 def callfunc_targets(node, merge):
633     objexpr, ident, direct_call, method_call, constructor, mod_var, parent_constr = analyze_callfunc(node)
634     funcs = []
635
636     if node.node in merge and [t for t in merge[node.node] if isinstance(t[0], function)]: # anonymous function call
637         funcs = [t[0] for t in merge[node.node] if isinstance(t[0], function)]
638
639     elif constructor:
640         if ident == 'defaultdict' and len(node.args) == 2:
641             funcs = [constructor.funcs['__initdict__']] # XXX __initi__
642         elif '__init__' in constructor.funcs: 
643             funcs = [constructor.funcs['__init__']]
644
645     elif parent_constr:
646         if ident != '__init__':
647             cl = inode(node).parent.parent
648             funcs = [cl.funcs[ident]]
649
650     elif direct_call:
651         funcs = [direct_call]
652
653     elif method_call:
654         classes = set([t[0] for t in merge[objexpr]])
655         funcs = [cl.funcs[ident] for cl in classes if ident in cl.funcs]
656
657     return funcs
658
659 def connect_actual_formal(expr, func, parent_constr=False, check_error=False):
660     pairs = []
661
662     actuals = [a for a in expr.args if not isinstance(a, Keyword)]
663     if isinstance(func.parent, class_): 
664         formals = [f for f in func.formals if not f in ['self', func.varargs, func.kwargs]]
665     else:
666         formals = [f for f in func.formals if not f in [func.varargs, func.kwargs]]
667     keywords = [a for a in expr.args if isinstance(a, Keyword)]
668
669     if parent_constr: actuals = actuals[1:] 
670
671     if check_error and func.ident not in ['min', 'max']:
672         if len(actuals)+len(keywords) > len(formals) and not func.varargs:
673             #if func.ident != 'join':
674             if not (func.mv.module.builtin and func.mv.module.ident == 'path' and func.ident == 'join'): # XXX
675                 error("too many arguments in call to '%s'" % func.ident, expr)
676         if len(actuals)+len(keywords) < len(formals)-len(func.defaults) and not expr.star_args:
677             error("not enough arguments in call to '%s'" % func.ident, expr)
678
679         missing = formals[len(actuals):-len(func.defaults)] 
680         if [x for x in missing if not x in [a.name for a in keywords]]:
681             error("no '%s' argument in call to '%s'" % (missing[0], func.ident))
682
683     kwdict = {}
684     for kw in keywords: 
685         if kw.name not in formals:
686             error("no argument '%s' in call to '%s'" % (kw.name, func.ident), expr)
687         kwdict[kw.name] = kw.expr
688
689     uglyoffset = len(func.defaults)-(len(formals)-len(actuals))
690
691     # --- connect regular, default and keyword arguments
692     if not func.mv.module.builtin or func.mv.module.ident in ['random', 'itertools', 'datetime'] or \
693         (func.ident in ('sort','sorted')): # XXX investigate
694         if not (func.mv.module.builtin and func.mv.module.ident == 'random' and func.ident == 'randrange'):
695             for (i, formal) in enumerate(formals[len(actuals):]):
696                 if formal in kwdict:
697                     actuals.append(kwdict[formal])
698                     continue
699
700                 if not func.defaults: # XXX
701                     continue
702                 default = func.defaults[i+uglyoffset]
703                 actuals.append(default)
704
705     for (actual, formal) in zip(actuals, formals):
706         pairs.append((actual, func.vars[formal]))
707
708     # --- actual star argument: unpack to extra formals
709     if expr.star_args: 
710         pairs.append((expr.star_args, tuple([func.vars[f] for f in formals[len(actuals):]])))
711
712     # --- formal star argument: pack actual arguments
713     if func.varargs:
714         pairs.append((tuple(actuals[len(formals):]), func.vars[func.varargs]))
715
716     return pairs
717
718 def parent_func(thing):
719     parent = inode(thing).parent
720     while parent:
721         if not isinstance(parent, function) or not parent.listcomp:
722             return parent
723         parent = parent.parent
724
725     return None
726
727 def register_tempvar(var, func): 
728     #print 'register tvar', var, func
729     if func:
730         func.registered_tempvars.append(var)
731         
732 def const_literal(node):
733     if isinstance(node, (UnarySub, UnaryAdd)):
734         node = node.expr
735     return isinstance(node, Const) and isinstance(node.value, (int, float))
736