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