1 #!/usr/bin/env python 2 3 """ 4 Common functions. 5 6 Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 7 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk> 8 9 This program is free software; you can redistribute it and/or modify it under 10 the terms of the GNU General Public License as published by the Free Software 11 Foundation; either version 3 of the License, or (at your option) any later 12 version. 13 14 This program is distributed in the hope that it will be useful, but WITHOUT 15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 details. 18 19 You should have received a copy of the GNU General Public License along with 20 this program. If not, see <http://www.gnu.org/licenses/>. 21 """ 22 23 from errors import * 24 from os import listdir, makedirs, remove 25 from os.path import exists, isdir, join, split 26 import compiler 27 28 class CommonOutput: 29 30 "Common output functionality." 31 32 def check_output(self): 33 34 "Check the existing output and remove it if irrelevant." 35 36 if not exists(self.output): 37 makedirs(self.output) 38 39 details = self.importer.get_cache_details() 40 recorded_details = self.get_output_details() 41 42 if recorded_details != details: 43 self.remove_output() 44 45 writefile(self.get_output_details_filename(), details) 46 47 def get_output_details_filename(self): 48 49 "Return the output details filename." 50 51 return join(self.output, "$details") 52 53 def get_output_details(self): 54 55 "Return details of the existing output." 56 57 details_filename = self.get_output_details_filename() 58 59 if not exists(details_filename): 60 return None 61 else: 62 return readfile(details_filename) 63 64 def remove_output(self, dirname=None): 65 66 "Remove the output." 67 68 dirname = dirname or self.output 69 70 for filename in listdir(dirname): 71 path = join(dirname, filename) 72 if isdir(path): 73 self.remove_output(path) 74 else: 75 remove(path) 76 77 class CommonModule: 78 79 "A common module representation." 80 81 def __init__(self, name, importer): 82 83 """ 84 Initialise this module with the given 'name' and an 'importer' which is 85 used to provide access to other modules when required. 86 """ 87 88 self.name = name 89 self.importer = importer 90 self.filename = None 91 92 # Inspection-related attributes. 93 94 self.astnode = None 95 self.iterators = {} 96 self.temp = {} 97 self.lambdas = {} 98 99 # Constants, literals and values. 100 101 self.constants = {} 102 self.constant_values = {} 103 self.literals = {} 104 self.literal_types = {} 105 106 # Nested namespaces. 107 108 self.namespace_path = [] 109 self.in_function = False 110 111 # Attribute chains. 112 113 self.attrs = [] 114 115 def __repr__(self): 116 return "CommonModule(%r, %r)" % (self.name, self.importer) 117 118 def parse_file(self, filename): 119 120 "Parse the file with the given 'filename', initialising attributes." 121 122 self.filename = filename 123 self.astnode = compiler.parseFile(filename) 124 125 # Module-relative naming. 126 127 def get_global_path(self, name): 128 return "%s.%s" % (self.name, name) 129 130 def get_namespace_path(self): 131 return ".".join([self.name] + self.namespace_path) 132 133 def get_object_path(self, name): 134 return ".".join([self.name] + self.namespace_path + [name]) 135 136 def get_parent_path(self): 137 return ".".join([self.name] + self.namespace_path[:-1]) 138 139 # Namespace management. 140 141 def enter_namespace(self, name): 142 143 "Enter the namespace having the given 'name'." 144 145 self.namespace_path.append(name) 146 147 def exit_namespace(self): 148 149 "Exit the current namespace." 150 151 self.namespace_path.pop() 152 153 # Constant reference naming. 154 155 def get_constant_name(self, value): 156 157 "Add a new constant to the current namespace for 'value'." 158 159 path = self.get_namespace_path() 160 init_item(self.constants, path, dict) 161 return "$c%d" % add_counter_item(self.constants[path], value) 162 163 # Literal reference naming. 164 165 def get_literal_name(self): 166 167 "Add a new literal to the current namespace." 168 169 path = self.get_namespace_path() 170 init_item(self.literals, path, lambda: 0) 171 return "$C%d" % self.literals[path] 172 173 def next_literal(self): 174 self.literals[self.get_namespace_path()] += 1 175 176 # Temporary iterator naming. 177 178 def get_iterator_path(self): 179 return self.in_function and self.get_namespace_path() or self.name 180 181 def get_iterator_name(self): 182 path = self.get_iterator_path() 183 init_item(self.iterators, path, lambda: 0) 184 return "$i%d" % self.iterators[path] 185 186 def next_iterator(self): 187 self.iterators[self.get_iterator_path()] += 1 188 189 # Temporary variable naming. 190 191 def get_temporary_name(self): 192 path = self.get_namespace_path() 193 init_item(self.temp, path, lambda: 0) 194 return "$t%d" % self.temp[path] 195 196 def next_temporary(self): 197 self.temp[self.get_namespace_path()] += 1 198 199 # Arbitrary function naming. 200 201 def get_lambda_name(self): 202 path = self.get_namespace_path() 203 init_item(self.lambdas, path, lambda: 0) 204 name = "$l%d" % self.lambdas[path] 205 self.lambdas[path] += 1 206 return name 207 208 def reset_lambdas(self): 209 self.lambdas = {} 210 211 # Constant and literal recording. 212 213 def get_constant_reference(self, ref, value): 214 215 "Return a constant reference for the given 'ref' type and 'value'." 216 217 constant_name = self.get_constant_name(value) 218 219 # Return a reference for the constant. 220 221 objpath = self.get_object_path(constant_name) 222 name_ref = ConstantValueRef(constant_name, ref.instance_of(), value) 223 224 # Record the value and type for the constant. 225 226 self.constant_values[objpath] = name_ref.value, name_ref.get_origin() 227 return name_ref 228 229 def get_literal_reference(self, name, ref, items, cls): 230 231 # Construct an invocation using the items as arguments. 232 233 typename = "$L%s" % name 234 235 invocation = compiler.ast.CallFunc( 236 compiler.ast.Name(typename), 237 items 238 ) 239 240 # Get a name for the actual literal. 241 242 instname = self.get_literal_name() 243 self.next_literal() 244 245 # Record the type for the literal. 246 247 objpath = self.get_object_path(instname) 248 self.literal_types[objpath] = ref.get_origin() 249 250 # Return a wrapper for the invocation exposing the items. 251 252 return cls( 253 instname, 254 ref.instance_of(), 255 self.process_structure_node(invocation), 256 invocation.args 257 ) 258 259 # Node handling. 260 261 def process_structure(self, node): 262 263 """ 264 Within the given 'node', process the program structure. 265 266 During inspection, this will process global declarations, adjusting the 267 module namespace, and import statements, building a module dependency 268 hierarchy. 269 270 During translation, this will consult deduced program information and 271 output translated code. 272 """ 273 274 l = [] 275 for n in node.getChildNodes(): 276 l.append(self.process_structure_node(n)) 277 return l 278 279 def process_augassign_node(self, n): 280 281 "Process the given augmented assignment node 'n'." 282 283 op = operator_functions[n.op] 284 285 if isinstance(n.node, compiler.ast.Getattr): 286 target = compiler.ast.AssAttr(n.node.expr, n.node.attrname, "OP_ASSIGN") 287 elif isinstance(n.node, compiler.ast.Name): 288 target = compiler.ast.AssName(n.node.name, "OP_ASSIGN") 289 else: 290 target = n.node 291 292 assignment = compiler.ast.Assign( 293 [target], 294 compiler.ast.CallFunc( 295 compiler.ast.Name("$op%s" % op), 296 [n.node, n.expr])) 297 298 return self.process_structure_node(assignment) 299 300 def process_assignment_for_function(self, original_name, name): 301 302 """ 303 Return an assignment operation making 'original_name' refer to the given 304 'name'. 305 """ 306 307 assignment = compiler.ast.Assign( 308 [compiler.ast.AssName(original_name, "OP_ASSIGN")], 309 compiler.ast.Name(name) 310 ) 311 312 return self.process_structure_node(assignment) 313 314 def process_assignment_node_items(self, n, expr): 315 316 """ 317 Process the given assignment node 'n' whose children are to be assigned 318 items of 'expr'. 319 """ 320 321 name_ref = self.process_structure_node(expr) 322 323 # Either unpack the items and present them directly to each assignment 324 # node. 325 326 if isinstance(name_ref, LiteralSequenceRef): 327 self.process_literal_sequence_items(n, name_ref) 328 329 # Or have the assignment nodes access each item via the sequence API. 330 331 else: 332 self.process_assignment_node_items_by_position(n, expr, name_ref) 333 334 def process_assignment_node_items_by_position(self, n, expr, name_ref): 335 336 """ 337 Process the given sequence assignment node 'n', converting the node to 338 the separate assignment of each target using positional access on a 339 temporary variable representing the sequence. Use 'expr' as the assigned 340 value and 'name_ref' as the reference providing any existing temporary 341 variable. 342 """ 343 344 assignments = [] 345 346 if isinstance(name_ref, NameRef): 347 temp = name_ref.name 348 else: 349 temp = self.get_temporary_name() 350 self.next_temporary() 351 352 assignments.append( 353 compiler.ast.Assign([compiler.ast.AssName(temp, "OP_ASSIGN")], expr) 354 ) 355 356 for i, node in enumerate(n.nodes): 357 assignments.append( 358 compiler.ast.Assign([node], compiler.ast.Subscript( 359 compiler.ast.Name(temp), "OP_APPLY", [compiler.ast.Const(i)])) 360 ) 361 362 return self.process_structure_node(compiler.ast.Stmt(assignments)) 363 364 def process_literal_sequence_items(self, n, name_ref): 365 366 """ 367 Process the given assignment node 'n', obtaining from the given 368 'name_ref' the items to be assigned to the assignment targets. 369 """ 370 371 if len(n.nodes) == len(name_ref.items): 372 for node, item in zip(n.nodes, name_ref.items): 373 self.process_assignment_node(node, item) 374 else: 375 raise InspectError("In %s, item assignment needing %d items is given %d items." % ( 376 self.get_namespace_path(), len(n.nodes), len(name_ref.items))) 377 378 def process_compare_node(self, n): 379 380 """ 381 Process the given comparison node 'n', converting an operator sequence 382 from... 383 384 <expr1> <op1> <expr2> <op2> <expr3> 385 386 ...to... 387 388 <op1>(<expr1>, <expr2>) and <op2>(<expr2>, <expr3>) 389 """ 390 391 invocations = [] 392 last = n.expr 393 394 for op, op_node in n.ops: 395 op = operator_functions.get(op) 396 397 invocations.append(compiler.ast.CallFunc( 398 compiler.ast.Name("$op%s" % op), 399 [last, op_node])) 400 401 last = op_node 402 403 if len(invocations) > 1: 404 result = compiler.ast.And(invocations) 405 else: 406 result = invocations[0] 407 408 return self.process_structure_node(result) 409 410 def process_dict_node(self, node): 411 412 """ 413 Process the given dictionary 'node', returning a list of (key, value) 414 tuples. 415 """ 416 417 l = [] 418 for key, value in node.items: 419 l.append(( 420 self.process_structure_node(key), 421 self.process_structure_node(value))) 422 return l 423 424 def process_for_node(self, n): 425 426 """ 427 Generate attribute accesses for {n.list}.__iter__ and the next method on 428 the iterator, producing a replacement node for the original. 429 """ 430 431 node = compiler.ast.Stmt([ 432 433 # <iterator> = {n.list}.__iter__ 434 435 compiler.ast.Assign( 436 [compiler.ast.AssName(self.get_iterator_name(), "OP_ASSIGN")], 437 compiler.ast.CallFunc( 438 compiler.ast.Getattr(n.list, "__iter__"), 439 [] 440 )), 441 442 # try: 443 # while True: 444 # <var>... = <iterator>.next() 445 # ... 446 # except StopIteration: 447 # pass 448 449 compiler.ast.TryExcept( 450 compiler.ast.While( 451 compiler.ast.Name("True"), 452 compiler.ast.Stmt([ 453 compiler.ast.Assign( 454 [n.assign], 455 compiler.ast.CallFunc( 456 compiler.ast.Getattr(compiler.ast.Name(self.get_iterator_name()), "next"), 457 [] 458 )), 459 n.body]), 460 None), 461 [(compiler.ast.Name("StopIteration"), None, compiler.ast.Stmt([compiler.ast.Pass()]))], 462 None) 463 ]) 464 465 self.next_iterator() 466 self.process_structure_node(node) 467 468 def convert_ifexp_node(self, n): 469 470 """ 471 Convert the given if expression node 'n'. An if expression is considered 472 as mapping to a function body containing an if statement as follows: 473 474 <expr> if <test> else <altexpr> 475 476 lambda <argnames>: 477 if <test>: 478 return <expr> 479 else: 480 return <altexpr> 481 482 The <argnames> are populated with defaults after the node has been 483 processed. 484 """ 485 486 node = compiler.ast.Lambda( 487 [], [], 0, 488 compiler.ast.If([ 489 (n.test, compiler.ast.Return(n.then)) 490 ], 491 compiler.ast.Return(n.else_) 492 )) 493 494 return node 495 496 def convert_listcomp_node(self, n): 497 498 """ 499 Convert the given list comprehension node 'n'. A list comprehension is 500 considered as mapping to a function body containing a for loop as 501 follows: 502 503 [<expr> for <varexpr1> in <list1> if <ifexpr1> for <varexpr2> in <list2> if <ifexpr2> if <ifexpr3>] 504 505 lambda <argnames>: 506 <result> = [] 507 for <varexpr1> in <list1>: 508 if <ifexpr1>: 509 for <varexpr2> in <list2>: 510 if <ifexpr2>: 511 if <ifexpr3>: 512 <result>.append(<expr>) 513 return <result> 514 515 The <argnames> are populated with defaults after the node has been 516 processed. 517 """ 518 519 temp = "$tr" 520 521 node = compiler.ast.Lambda( 522 [], [], 0, 523 compiler.ast.Stmt([ 524 525 # <result> = [] 526 527 compiler.ast.Assign([compiler.ast.AssName(temp, "OP_ASSIGN")], 528 compiler.ast.List([]) 529 ), 530 531 # for ... 532 533 self.convert_listcomp_for_node(n.quals[0], n.quals[1:], n.expr, temp), 534 535 # return <result> 536 537 compiler.ast.Return(compiler.ast.Name(temp)) 538 ])) 539 540 return node 541 542 def convert_listcomp_for_node(self, loop, following_loops, expr, temp): 543 544 """ 545 Return a node representing 'loop', encapsulating 'following_loops' and 546 employing 'expr' in the innermost loop body appending to 'temp'. 547 """ 548 549 if loop.ifs: 550 body = self.convert_listcomp_if_node(loop.ifs[0], loop.ifs[1:], following_loops, expr, temp) 551 elif following_loops: 552 body = self.convert_listcomp_for_node(following_loops[0], following_loops[1:], expr, temp) 553 else: 554 body = self.convert_listcomp_body_node(expr, temp) 555 556 return compiler.ast.For(loop.assign, loop.list, compiler.ast.Stmt([body]), None) 557 558 def convert_listcomp_if_node(self, if_, following_ifs, following_loops, expr, temp): 559 560 """ 561 Return a node representing 'if_', encapsulating the 'following_ifs' and 562 'following_loops' and employing 'expr' in the innermost loop body 563 appending to 'temp'. 564 """ 565 566 if following_ifs: 567 body = self.convert_listcomp_if_node(following_ifs[0], following_ifs[1:], following_loops, expr, temp) 568 elif following_loops: 569 body = self.convert_listcomp_for_node(following_loops[0], following_loops[1:], expr, temp) 570 else: 571 body = self.convert_listcomp_body_node(expr, temp) 572 573 return compiler.ast.If([(if_.test, compiler.ast.Stmt([body]))], None) 574 575 def convert_listcomp_body_node(self, expr, temp): 576 577 "Return a node appending 'expr' to 'temp'." 578 579 return compiler.ast.Discard( 580 compiler.ast.CallFunc( 581 compiler.ast.Getattr(compiler.ast.Name(temp), "append"), [expr])) 582 583 def process_literal_sequence_node(self, n, name, ref, cls): 584 585 """ 586 Process the given literal sequence node 'n' as a function invocation, 587 with 'name' indicating the type of the sequence, and 'ref' being a 588 reference to the type. The 'cls' is used to instantiate a suitable name 589 reference. 590 """ 591 592 if name == "dict": 593 items = [] 594 for key, value in n.items: 595 items.append(compiler.ast.Tuple([key, value])) 596 else: # name in ("list", "tuple"): 597 items = n.nodes 598 599 return self.get_literal_reference(name, ref, items, cls) 600 601 def process_operator_node(self, n): 602 603 """ 604 Process the given operator node 'n' as an operator function invocation. 605 """ 606 607 op = operator_functions[n.__class__.__name__] 608 invocation = compiler.ast.CallFunc( 609 compiler.ast.Name("$op%s" % op), 610 list(n.getChildNodes()) 611 ) 612 return self.process_structure_node(invocation) 613 614 def process_slice_node(self, n, expr=None): 615 616 """ 617 Process the given slice node 'n' as an operator function invocation. 618 """ 619 620 op = n.flags == "OP_ASSIGN" and "setslice" or "getslice" 621 invocation = compiler.ast.CallFunc( 622 compiler.ast.Name("$op%s" % op), 623 [n.expr, n.lower or compiler.ast.Name("None"), n.upper or compiler.ast.Name("None")] + 624 (expr and [expr] or []) 625 ) 626 return self.process_structure_node(invocation) 627 628 def process_sliceobj_node(self, n): 629 630 """ 631 Process the given slice object node 'n' as a slice constructor. 632 """ 633 634 op = "slice" 635 invocation = compiler.ast.CallFunc( 636 compiler.ast.Name("$op%s" % op), 637 n.nodes 638 ) 639 return self.process_structure_node(invocation) 640 641 def process_subscript_node(self, n, expr=None): 642 643 """ 644 Process the given subscript node 'n' as an operator function invocation. 645 """ 646 647 op = n.flags == "OP_ASSIGN" and "setitem" or "getitem" 648 invocation = compiler.ast.CallFunc( 649 compiler.ast.Name("$op%s" % op), 650 [n.expr] + list(n.subs) + (expr and [expr] or []) 651 ) 652 return self.process_structure_node(invocation) 653 654 def process_attribute_chain(self, n): 655 656 """ 657 Process the given attribute access node 'n'. Return a reference 658 describing the expression. 659 """ 660 661 # AssAttr/Getattr are nested with the outermost access being the last 662 # access in any chain. 663 664 self.attrs.insert(0, n.attrname) 665 attrs = self.attrs 666 667 # Break attribute chains where non-access nodes are found. 668 669 if not self.have_access_expression(n): 670 self.attrs = [] 671 672 # Descend into the expression, extending backwards any existing chain, 673 # or building another for the expression. 674 675 name_ref = self.process_structure_node(n.expr) 676 677 # Restore chain information applying to this node. 678 679 self.attrs = attrs 680 681 # Return immediately if the expression was another access and thus a 682 # continuation backwards along the chain. The above processing will 683 # have followed the chain all the way to its conclusion. 684 685 if self.have_access_expression(n): 686 del self.attrs[0] 687 688 return name_ref 689 690 def have_access_expression(self, node): 691 692 "Return whether the expression associated with 'node' is Getattr." 693 694 return isinstance(node.expr, compiler.ast.Getattr) 695 696 def get_name_for_tracking(self, name, path=None): 697 698 """ 699 Return the name to be used for attribute usage observations involving 700 the given 'name' in the current namespace. If 'path' is indicated and 701 the name is being used outside a function, return the path value; 702 otherwise, return a path computed using the current namespace and the 703 given name. 704 705 The intention of this method is to provide a suitably-qualified name 706 that can be tracked across namespaces. Where globals are being 707 referenced in class namespaces, they should be referenced using their 708 path within the module, not using a path within each class. 709 710 It may not be possible to identify a global within a function at the 711 time of inspection (since a global may appear later in a file). 712 Consequently, globals are identified by their local name rather than 713 their module-qualified path. 714 """ 715 716 # For functions, use the appropriate local names. 717 718 if self.in_function: 719 return name 720 721 # For static namespaces, use the given qualified name. 722 723 elif path: 724 return path 725 726 # Otherwise, establish a name in the current (module) namespace. 727 728 else: 729 return self.get_object_path(name) 730 731 def get_path_for_access(self): 732 733 "Outside functions, register accesses at the module level." 734 735 if not self.in_function: 736 return self.name 737 else: 738 return self.get_namespace_path() 739 740 def get_module_name(self, node): 741 742 """ 743 Using the given From 'node' in this module, calculate any relative import 744 information, returning a tuple containing a module to import along with any 745 names to import based on the node's name information. 746 747 Where the returned module is given as None, whole module imports should 748 be performed for the returned modules using the returned names. 749 """ 750 751 # Absolute import. 752 753 if node.level == 0: 754 return node.modname, node.names 755 756 # Relative to an ancestor of this module. 757 758 else: 759 path = self.name.split(".") 760 level = node.level 761 762 # Relative imports treat package roots as submodules. 763 764 if split(self.filename)[-1] == "__init__.py": 765 level -= 1 766 767 if level > len(path): 768 raise InspectError("Relative import %r involves too many levels up from module %r" % ( 769 ("%s%s" % ("." * node.level, node.modname or "")), self.name)) 770 771 basename = ".".join(path[:len(path)-level]) 772 773 # Name imports from a module. 774 775 if node.modname: 776 return "%s.%s" % (basename, node.modname), node.names 777 778 # Relative whole module imports. 779 780 else: 781 return basename, node.names 782 783 def get_argnames(args): 784 785 """ 786 Return a list of all names provided by 'args'. Since tuples may be 787 employed, the arguments are traversed depth-first. 788 """ 789 790 l = [] 791 for arg in args: 792 if isinstance(arg, tuple): 793 l += get_argnames(arg) 794 else: 795 l.append(arg) 796 return l 797 798 # Classes representing inspection and translation observations. 799 800 class Result: 801 802 "An abstract expression result." 803 804 def is_name(self): 805 return False 806 def get_origin(self): 807 return None 808 809 class NameRef(Result): 810 811 "A reference to a name." 812 813 def __init__(self, name, expr=None): 814 self.name = name 815 self.expr = expr 816 817 def is_name(self): 818 return True 819 820 def reference(self): 821 return None 822 823 def final(self): 824 return None 825 826 def __repr__(self): 827 return "NameRef(%r, %r)" % (self.name, self.expr) 828 829 class LocalNameRef(NameRef): 830 831 "A reference to a local name." 832 833 def __init__(self, name, number): 834 NameRef.__init__(self, name) 835 self.number = number 836 837 def __repr__(self): 838 return "LocalNameRef(%r, %r)" % (self.name, self.number) 839 840 class ResolvedNameRef(NameRef): 841 842 "A resolved name-based reference." 843 844 def __init__(self, name, ref, expr=None): 845 NameRef.__init__(self, name, expr) 846 self.ref = ref 847 848 def reference(self): 849 return self.ref 850 851 def get_name(self): 852 return self.ref and self.ref.get_name() or None 853 854 def get_origin(self): 855 return self.ref and self.ref.get_origin() or None 856 857 def static(self): 858 return self.ref and self.ref.static() or None 859 860 def final(self): 861 return self.ref and self.ref.final() or None 862 863 def has_kind(self, kinds): 864 return self.ref and self.ref.has_kind(kinds) 865 866 def __repr__(self): 867 return "ResolvedNameRef(%r, %r, %r)" % (self.name, self.ref, self.expr) 868 869 class ConstantValueRef(ResolvedNameRef): 870 871 "A constant reference representing a single literal value." 872 873 def __init__(self, name, ref, value, number=None): 874 ResolvedNameRef.__init__(self, name, ref) 875 self.value = value 876 self.number = number 877 878 def __repr__(self): 879 return "ConstantValueRef(%r, %r, %r, %r)" % (self.name, self.ref, self.value, self.number) 880 881 class InstanceRef(Result): 882 883 "An instance reference." 884 885 def __init__(self, ref): 886 self.ref = ref 887 888 def reference(self): 889 return self.ref 890 891 def __repr__(self): 892 return "InstanceRef(%r)" % self.ref 893 894 class LiteralSequenceRef(ResolvedNameRef): 895 896 "A reference representing a sequence of values." 897 898 def __init__(self, name, ref, node, items=None): 899 ResolvedNameRef.__init__(self, name, ref) 900 self.node = node 901 self.items = items 902 903 def __repr__(self): 904 return "LiteralSequenceRef(%r, %r, %r, %r)" % (self.name, self.ref, self.node, self.items) 905 906 # Dictionary utilities. 907 908 def init_item(d, key, fn): 909 910 """ 911 Add to 'd' an entry for 'key' using the callable 'fn' to make an initial 912 value where no entry already exists. 913 """ 914 915 if not d.has_key(key): 916 d[key] = fn() 917 return d[key] 918 919 def dict_for_keys(d, keys): 920 921 "Return a new dictionary containing entries from 'd' for the given 'keys'." 922 923 nd = {} 924 for key in keys: 925 if d.has_key(key): 926 nd[key] = d[key] 927 return nd 928 929 def make_key(s): 930 931 "Make sequence 's' into a tuple-based key, first sorting its contents." 932 933 l = list(s) 934 l.sort() 935 return tuple(l) 936 937 def add_counter_item(d, key): 938 939 """ 940 Make a mapping in 'd' for 'key' to the number of keys added before it, thus 941 maintaining a mapping of keys to their order of insertion. 942 """ 943 944 if not d.has_key(key): 945 d[key] = len(d.keys()) 946 return d[key] 947 948 def remove_items(d1, d2): 949 950 "Remove from 'd1' all items from 'd2'." 951 952 for key in d2.keys(): 953 if d1.has_key(key): 954 del d1[key] 955 956 # Set utilities. 957 958 def first(s): 959 return list(s)[0] 960 961 def same(s1, s2): 962 return set(s1) == set(s2) 963 964 # General input/output. 965 966 def readfile(filename): 967 968 "Return the contents of 'filename'." 969 970 f = open(filename) 971 try: 972 return f.read() 973 finally: 974 f.close() 975 976 def writefile(filename, s): 977 978 "Write to 'filename' the string 's'." 979 980 f = open(filename, "w") 981 try: 982 f.write(s) 983 finally: 984 f.close() 985 986 # General encoding. 987 988 def sorted_output(x): 989 990 "Sort sequence 'x' and return a string with commas separating the values." 991 992 x = map(str, x) 993 x.sort() 994 return ", ".join(x) 995 996 # Attribute chain decoding. 997 998 def get_attrnames(attrnames): 999 if attrnames.startswith("#"): 1000 return [attrnames] 1001 else: 1002 return attrnames.split(".") 1003 1004 def get_attrname_from_location(location): 1005 path, name, attrnames, access = location 1006 return get_attrnames(attrnames)[0] 1007 1008 # Useful data. 1009 1010 predefined_constants = "Ellipsis", "False", "None", "NotImplemented", "True" 1011 1012 operator_functions = { 1013 1014 # Fundamental operations. 1015 1016 "is" : "is_", 1017 "is not" : "is_not", 1018 1019 # Binary operations. 1020 1021 "in" : "in_", 1022 "not in" : "not_in", 1023 "Add" : "add", 1024 "Bitand" : "and_", 1025 "Bitor" : "or_", 1026 "Bitxor" : "xor", 1027 "Div" : "div", 1028 "FloorDiv" : "floordiv", 1029 "LeftShift" : "lshift", 1030 "Mod" : "mod", 1031 "Mul" : "mul", 1032 "Power" : "pow", 1033 "RightShift" : "rshift", 1034 "Sub" : "sub", 1035 1036 # Unary operations. 1037 1038 "Invert" : "invert", 1039 "UnaryAdd" : "pos", 1040 "UnarySub" : "neg", 1041 1042 # Augmented assignment. 1043 1044 "+=" : "iadd", 1045 "-=" : "isub", 1046 "*=" : "imul", 1047 "/=" : "idiv", 1048 "//=" : "ifloordiv", 1049 "%=" : "imod", 1050 "**=" : "ipow", 1051 "<<=" : "ilshift", 1052 ">>=" : "irshift", 1053 "&=" : "iand", 1054 "^=" : "ixor", 1055 "|=" : "ior", 1056 1057 # Comparisons. 1058 1059 "==" : "eq", 1060 "!=" : "ne", 1061 "<" : "lt", 1062 "<=" : "le", 1063 ">=" : "ge", 1064 ">" : "gt", 1065 } 1066 1067 # vim: tabstop=4 expandtab shiftwidth=4