1 #!/usr/bin/env python 2 3 """ 4 Generate C code from object layouts and other deduced information. 5 6 Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from common import CommonOutput, get_builtin_module, get_builtin_type 23 from encoders import encode_bound_reference, encode_function_pointer, \ 24 encode_instantiator_pointer, \ 25 encode_literal_constant, encode_literal_constant_member, \ 26 encode_literal_constant_value, \ 27 encode_literal_data_initialiser, \ 28 encode_literal_instantiator, encode_literal_reference, \ 29 encode_path, \ 30 encode_predefined_reference, encode_size, \ 31 encode_symbol, encode_tablename, \ 32 encode_type_attribute, decode_type_attribute, \ 33 is_type_attribute 34 from os import listdir, mkdir 35 from os.path import exists, isdir, join, split 36 from referencing import Reference 37 38 def copy(source, target): 39 40 "Copy a text file from 'source' to 'target'." 41 42 if isdir(target): 43 target = join(target, split(source)[-1]) 44 infile = open(source) 45 outfile = open(target, "w") 46 try: 47 outfile.write(infile.read()) 48 finally: 49 outfile.close() 50 infile.close() 51 52 class Generator(CommonOutput): 53 54 "A code generator." 55 56 # NOTE: These must be synchronised with the library. 57 58 function_type = "__builtins__.core.function" 59 string_type = "__builtins__.str.string" 60 type_type = "__builtins__.core.type" 61 62 predefined_constant_members = ( 63 ("__builtins__.boolean", "False"), 64 ("__builtins__.boolean", "True"), 65 ("__builtins__.none", "None"), 66 ("__builtins__.notimplemented", "NotImplemented"), 67 ) 68 69 literal_mapping_types = ( 70 "__builtins__.dict.dict", 71 ) 72 73 literal_sequence_types = ( 74 "__builtins__.list.list", 75 "__builtins__.tuple.tuple", 76 ) 77 78 literal_instantiator_types = literal_mapping_types + literal_sequence_types 79 80 def __init__(self, importer, optimiser, output): 81 82 """ 83 Initialise the generator with the given 'importer', 'optimiser' and 84 'output' directory. 85 """ 86 87 self.importer = importer 88 self.optimiser = optimiser 89 self.output = output 90 91 def to_output(self, debug=False): 92 93 "Write the generated code." 94 95 self.check_output() 96 self.write_structures() 97 self.copy_templates(debug) 98 99 def copy_templates(self, debug=False): 100 101 "Copy template files to the generated output directory." 102 103 templates = join(split(__file__)[0], "templates") 104 105 for filename in listdir(templates): 106 target = self.output 107 108 # Handle debug resources. 109 110 if filename.endswith("-debug"): 111 if debug: 112 target = join(self.output, filename[:-len("-debug")]) 113 else: 114 continue 115 116 # Handle non-debug resources. 117 118 if debug and exists(join(templates, "%s-debug" % filename)): 119 continue 120 121 pathname = join(templates, filename) 122 123 # Copy files into the target directory. 124 125 if not isdir(pathname): 126 copy(pathname, target) 127 128 # Copy directories (such as the native code directory). 129 130 else: 131 target = join(self.output, filename) 132 133 if not exists(target): 134 mkdir(target) 135 136 for filename in listdir(pathname): 137 copy(join(pathname, filename), target) 138 139 def write_structures(self): 140 141 "Write structures used by the program." 142 143 f_consts = open(join(self.output, "progconsts.h"), "w") 144 f_defs = open(join(self.output, "progtypes.c"), "w") 145 f_decls = open(join(self.output, "progtypes.h"), "w") 146 f_signatures = open(join(self.output, "main.h"), "w") 147 f_code = open(join(self.output, "main.c"), "w") 148 149 try: 150 # Output boilerplate. 151 152 print >>f_consts, """\ 153 #ifndef __PROGCONSTS_H__ 154 #define __PROGCONSTS_H__ 155 """ 156 print >>f_decls, """\ 157 #ifndef __PROGTYPES_H__ 158 #define __PROGTYPES_H__ 159 160 #include "progconsts.h" 161 #include "types.h" 162 """ 163 print >>f_defs, """\ 164 #include "progtypes.h" 165 #include "progops.h" 166 #include "main.h" 167 """ 168 print >>f_signatures, """\ 169 #ifndef __MAIN_H__ 170 #define __MAIN_H__ 171 172 #include "types.h" 173 """ 174 print >>f_code, """\ 175 #include <string.h> 176 #include <stdio.h> 177 #include "types.h" 178 #include "exceptions.h" 179 #include "ops.h" 180 #include "progconsts.h" 181 #include "progtypes.h" 182 #include "progops.h" 183 #include "main.h" 184 """ 185 186 # Generate table and structure data. 187 188 function_instance_attrs = None 189 objects = self.optimiser.attr_table.items() 190 objects.sort() 191 192 self.callables = {} 193 194 for ref, indexes in objects: 195 attrnames = self.get_attribute_names(indexes) 196 197 kind = ref.get_kind() 198 path = ref.get_origin() 199 table_name = encode_tablename(kind, path) 200 structure_size = encode_size(kind, path) 201 202 # Generate structures for classes and modules. 203 204 if kind != "<instance>": 205 structure = [] 206 attrs = self.get_static_attributes(kind, path, attrnames) 207 208 # Set a special instantiator on the class. 209 210 if kind == "<class>": 211 212 # Write instantiator declarations based on the 213 # applicable initialiser. 214 215 init_ref = attrs["__init__"] 216 217 # Write instantiator definitions. 218 219 self.write_instantiator(f_code, f_signatures, path, init_ref) 220 221 # Record the callable for parameter table generation. 222 223 self.callables[path] = init_ref.get_origin() 224 225 # Define special attributes. 226 227 attrs["__fn__"] = path 228 attrs["__args__"] = encode_size("pmin", path) 229 230 self.populate_structure(Reference(kind, path), attrs, kind, structure) 231 232 if kind == "<class>": 233 self.write_instance_structure(f_decls, path, structure_size) 234 235 self.write_structure(f_decls, f_defs, path, table_name, structure, 236 kind == "<class>" and path) 237 238 # Record function instance details for function generation below. 239 240 else: 241 attrs = self.get_instance_attributes(path, attrnames) 242 if path == self.function_type: 243 function_instance_attrs = attrs 244 245 # Record the callable for parameter table generation. 246 247 self.callables[path] = path 248 249 # Write a table for all objects. 250 251 table = [] 252 self.populate_table(Reference(kind, path), table) 253 self.write_table(f_decls, f_defs, table_name, structure_size, table) 254 255 # Generate function instances. 256 257 functions = self.importer.function_parameters.keys() 258 functions.sort() 259 extra_function_instances = [] 260 261 for path in functions: 262 263 # Instantiators are generated above. 264 265 if self.importer.classes.has_key(path) or not self.importer.get_object(path): 266 continue 267 268 # Record the callable for parameter table generation. 269 270 self.callables[path] = path 271 272 # Define the structure details. 273 274 cls = self.function_type 275 table_name = encode_tablename("<instance>", cls) 276 structure_size = encode_size("<instance>", path) 277 278 # Set a special callable attribute on the instance. 279 280 function_instance_attrs["__fn__"] = path 281 function_instance_attrs["__args__"] = encode_size("pmin", path) 282 283 # Produce two structures where a method is involved. 284 285 parent, name = path.rsplit(".", 1) 286 parent_ref = self.importer.get_object(parent) 287 parent_kind = parent_ref and parent_ref.get_kind() 288 289 # Populate and write each structure. 290 291 if parent_kind == "<class>": 292 293 # A bound version of a method. 294 295 structure = self.populate_function(path, function_instance_attrs, False) 296 self.write_structure(f_decls, f_defs, encode_bound_reference(path), table_name, structure) 297 298 # An unbound version of a method. 299 300 structure = self.populate_function(path, function_instance_attrs, True) 301 self.write_structure(f_decls, f_defs, path, table_name, structure) 302 303 else: 304 # A normal function. 305 306 structure = self.populate_function(path, function_instance_attrs, False) 307 self.write_structure(f_decls, f_defs, path, table_name, structure) 308 309 # Functions with defaults need to declare instance structures. 310 311 if self.importer.function_defaults.get(path): 312 self.write_instance_structure(f_decls, path, structure_size) 313 extra_function_instances.append(path) 314 315 # Write function declarations. 316 # Signature: __attr <name>(__attr[]); 317 318 print >>f_signatures, "__attr %s(__attr args[]);" % encode_function_pointer(path) 319 320 # Consolidate parameter tables for instantiators and functions. 321 322 parameter_tables = set() 323 324 for path, function_path in self.callables.items(): 325 parameters = self.optimiser.parameters[function_path] 326 if not parameters: 327 parameters = () 328 else: 329 parameters = tuple(parameters) 330 parameter_tables.add(parameters) 331 332 # Generate parameter tables for distinct function signatures. 333 334 for parameters in parameter_tables: 335 self.make_parameter_table(f_decls, f_defs, parameters) 336 337 # Generate predefined constants. 338 339 for path, name in self.predefined_constant_members: 340 self.make_predefined_constant(f_decls, f_defs, path, name) 341 342 # Generate literal constants. 343 344 for value, n in self.optimiser.constants.items(): 345 self.make_literal_constant(f_decls, f_defs, n, value) 346 347 # Finish the main source file. 348 349 self.write_main_program(f_code, f_signatures) 350 351 # Record size information for certain function instances as well as 352 # for classes, modules and other instances. 353 354 size_tables = {} 355 356 for kind in ["<class>", "<module>", "<instance>"]: 357 size_tables[kind] = {} 358 359 # Generate structure size data. 360 361 for ref, structure in self.optimiser.structures.items(): 362 size_tables[ref.get_kind()][ref.get_origin()] = len(structure) 363 364 for path in extra_function_instances: 365 defaults = self.importer.function_defaults[path] 366 size_tables["<instance>"][path] = size_tables["<instance>"][self.function_type] + len(defaults) 367 368 size_tables = size_tables.items() 369 size_tables.sort() 370 371 for kind, sizes in size_tables: 372 self.write_size_constants(f_consts, kind, sizes, 0) 373 374 # Generate parameter table size data. 375 376 min_sizes = {} 377 max_sizes = {} 378 379 # Determine the minimum number of parameters for each 380 381 for path in self.optimiser.parameters.keys(): 382 argmin, argmax = self.get_argument_limits(path) 383 min_sizes[path] = argmin 384 385 # Use the parameter table details to define the maximum number. 386 # The context is already present in the collection. 387 388 for parameters in parameter_tables: 389 signature = self.get_parameter_signature(parameters) 390 max_sizes[signature] = len(parameters) 391 392 self.write_size_constants(f_consts, "pmin", min_sizes, 0) 393 self.write_size_constants(f_consts, "pmax", max_sizes, 0) 394 395 # Generate parameter codes. 396 397 self.write_code_constants(f_consts, self.optimiser.all_paramnames, self.optimiser.arg_locations, "pcode", "ppos") 398 399 # Generate attribute codes. 400 401 self.write_code_constants(f_consts, self.optimiser.all_attrnames, self.optimiser.locations, "code", "pos") 402 403 # Output more boilerplate. 404 405 print >>f_consts, """\ 406 407 #endif /* __PROGCONSTS_H__ */""" 408 409 print >>f_decls, """\ 410 411 #define __FUNCTION_TYPE %s 412 #define __FUNCTION_INSTANCE_SIZE %s 413 #define __TYPE_CLASS_TYPE %s 414 #define __TYPE_CLASS_POS %s 415 #define __TYPE_CLASS_CODE %s 416 417 #endif /* __PROGTYPES_H__ */""" % ( 418 encode_path(self.function_type), 419 encode_size("<instance>", self.function_type), 420 encode_path(self.type_type), 421 encode_symbol("pos", encode_type_attribute(self.type_type)), 422 encode_symbol("code", encode_type_attribute(self.type_type)), 423 ) 424 425 print >>f_signatures, """\ 426 427 #endif /* __MAIN_H__ */""" 428 429 finally: 430 f_consts.close() 431 f_defs.close() 432 f_decls.close() 433 f_signatures.close() 434 f_code.close() 435 436 def make_literal_constant(self, f_decls, f_defs, n, value): 437 438 """ 439 Write literal constant details to 'f_decls' (to declare a structure) and 440 to 'f_defs' (to define the contents) for the constant with the number 441 'n' with the given literal 'value'. 442 """ 443 444 const_path = encode_literal_constant(n) 445 structure_name = encode_literal_reference(n) 446 447 # NOTE: This makes assumptions about the __builtins__ structure. 448 449 typename = get_builtin_type(value.__class__.__name__) 450 modname = get_builtin_module(typename) 451 ref = Reference("<instance>", "__builtins__.%s.%s" % (modname, typename)) 452 453 self.make_constant(f_decls, f_defs, ref, const_path, structure_name, value) 454 455 def make_predefined_constant(self, f_decls, f_defs, path, name): 456 457 """ 458 Write predefined constant details to 'f_decls' (to declare a structure) 459 and to 'f_defs' (to define the contents) for the constant located in 460 'path' with the given 'name'. 461 """ 462 463 # Determine the details of the constant. 464 465 attr_path = "%s.%s" % (path, name) 466 structure_name = encode_predefined_reference(attr_path) 467 ref = self.importer.get_object(attr_path) 468 469 self.make_constant(f_decls, f_defs, ref, attr_path, structure_name) 470 471 def make_constant(self, f_decls, f_defs, ref, const_path, structure_name, data=None): 472 473 """ 474 Write constant details to 'f_decls' (to declare a structure) and to 475 'f_defs' (to define the contents) for the constant described by 'ref' 476 having the given 'path' and 'structure_name' (for the constant structure 477 itself). 478 """ 479 480 # Obtain the attributes. 481 482 cls = ref.get_origin() 483 indexes = self.optimiser.attr_table[ref] 484 attrnames = self.get_attribute_names(indexes) 485 attrs = self.get_instance_attributes(cls, attrnames) 486 487 # Set the data, if provided. 488 489 if data is not None: 490 attrs["__data__"] = data 491 492 # Also set a key for dynamic attribute lookup, if a string. 493 494 if cls == self.string_type: 495 if data in self.optimiser.all_attrnames: 496 attrs["__key__"] = data 497 else: 498 attrs["__key__"] = None 499 500 # Define the structure details. An object is created for the constant, 501 # but an attribute is provided, referring to the object, for access to 502 # the constant in the program. 503 504 structure = [] 505 table_name = encode_tablename("<instance>", cls) 506 self.populate_structure(ref, attrs, ref.get_kind(), structure) 507 self.write_structure(f_decls, f_defs, structure_name, table_name, structure) 508 509 # Define a macro for the constant. 510 511 attr_name = encode_path(const_path) 512 print >>f_decls, "#define %s ((__attr) {&%s, &%s})" % (attr_name, structure_name, structure_name) 513 514 def make_parameter_table(self, f_decls, f_defs, parameters): 515 516 """ 517 Write parameter table details to 'f_decls' (to declare a table) and to 518 'f_defs' (to define the contents) for the given 'parameters'. 519 """ 520 521 # Use a signature for the table name instead of a separate name for each 522 # function. 523 524 signature = self.get_parameter_signature(parameters) 525 table_name = encode_tablename("<function>", signature) 526 structure_size = encode_size("pmax", signature) 527 528 table = [] 529 self.populate_parameter_table(parameters, table) 530 self.write_parameter_table(f_decls, f_defs, table_name, structure_size, table) 531 532 def get_parameter_signature(self, parameters): 533 534 "Return a signature for the given 'parameters'." 535 536 l = [] 537 for parameter in parameters: 538 if parameter is None: 539 l.append("") 540 else: 541 name, pos = parameter 542 l.append("%s_%s" % (name, pos)) 543 return l and "__".join(l) or "__void" 544 545 def get_signature_for_callable(self, path): 546 547 "Return the signature for the callable with the given 'path'." 548 549 function_path = self.callables[path] 550 parameters = self.optimiser.parameters[function_path] 551 return self.get_parameter_signature(parameters) 552 553 def write_size_constants(self, f_consts, size_prefix, sizes, padding): 554 555 """ 556 Write size constants to 'f_consts' for the given 'size_prefix', using 557 the 'sizes' dictionary to populate the definition, adding the given 558 'padding' to the basic sizes. 559 """ 560 561 print >>f_consts, "enum %s {" % encode_size(size_prefix) 562 first = True 563 for path, size in sizes.items(): 564 if not first: 565 print >>f_consts, "," 566 else: 567 first = False 568 f_consts.write(" %s = %d" % (encode_size(size_prefix, path), size + padding)) 569 print >>f_consts, "\n };" 570 571 def write_code_constants(self, f_consts, attrnames, locations, code_prefix, pos_prefix): 572 573 """ 574 Write code constants to 'f_consts' for the given 'attrnames' and 575 attribute 'locations'. 576 """ 577 578 print >>f_consts, "enum %s {" % encode_symbol(code_prefix) 579 first = True 580 for i, attrname in enumerate(attrnames): 581 if not first: 582 print >>f_consts, "," 583 else: 584 first = False 585 f_consts.write(" %s = %d" % (encode_symbol(code_prefix, attrname), i)) 586 print >>f_consts, "\n };" 587 588 print >>f_consts, "enum %s {" % encode_symbol(pos_prefix) 589 first = True 590 for i, attrnames in enumerate(locations): 591 for attrname in attrnames: 592 if not first: 593 print >>f_consts, "," 594 else: 595 first = False 596 f_consts.write(" %s = %d" % (encode_symbol(pos_prefix, attrname), i)) 597 print >>f_consts, "\n };" 598 599 def write_table(self, f_decls, f_defs, table_name, structure_size, table): 600 601 """ 602 Write the declarations to 'f_decls' and definitions to 'f_defs' for 603 the object having the given 'table_name' and the given 'structure_size', 604 with 'table' details used to populate the definition. 605 """ 606 607 print >>f_decls, "extern const __table %s;\n" % table_name 608 609 # Write the corresponding definition. 610 611 print >>f_defs, "const __table %s = {\n %s,\n {\n %s\n }\n };\n" % ( 612 table_name, structure_size, 613 ",\n ".join(table)) 614 615 def write_parameter_table(self, f_decls, f_defs, table_name, structure_size, table): 616 617 """ 618 Write the declarations to 'f_decls' and definitions to 'f_defs' for 619 the object having the given 'table_name' and the given 'structure_size', 620 with 'table' details used to populate the definition. 621 """ 622 623 print >>f_decls, "extern const __ptable %s;\n" % table_name 624 625 # Write the corresponding definition. 626 627 print >>f_defs, "const __ptable %s = {\n %s,\n {\n %s\n }\n };\n" % ( 628 table_name, structure_size, 629 ",\n ".join([("{%s, %s}" % t) for t in table])) 630 631 def write_instance_structure(self, f_decls, path, structure_size): 632 633 """ 634 Write a declaration to 'f_decls' for the object having the given 'path' 635 and the given 'structure_size'. 636 """ 637 638 # Write an instance-specific type definition for instances of classes. 639 # See: templates/types.h 640 641 print >>f_decls, """\ 642 typedef struct { 643 const __table * table; 644 unsigned int pos; 645 __attr attrs[%s]; 646 } %s; 647 """ % (structure_size, encode_symbol("obj", path)) 648 649 def write_structure(self, f_decls, f_defs, structure_name, table_name, structure, path=None): 650 651 """ 652 Write the declarations to 'f_decls' and definitions to 'f_defs' for 653 the object having the given 'structure_name', the given 'table_name', 654 and the given 'structure' details used to populate the definition. 655 """ 656 657 if f_decls: 658 print >>f_decls, "extern __obj %s;\n" % encode_path(structure_name) 659 660 is_class = path and self.importer.get_object(path).has_kind("<class>") 661 pos = is_class and encode_symbol("pos", encode_type_attribute(path)) or "0" 662 663 print >>f_defs, """\ 664 __obj %s = { 665 &%s, 666 %s, 667 { 668 %s 669 }}; 670 """ % ( 671 encode_path(structure_name), table_name, pos, 672 ",\n ".join(structure)) 673 674 def get_argument_limits(self, path): 675 676 """ 677 Return the argument minimum and maximum for the callable at 'path', 678 adding an argument position for a universal context. 679 """ 680 681 parameters = self.importer.function_parameters[path] 682 defaults = self.importer.function_defaults.get(path) 683 num_parameters = len(parameters) + 1 684 return num_parameters - (defaults and len(defaults) or 0), num_parameters 685 686 def get_attribute_names(self, indexes): 687 688 """ 689 Given a list of attribute table 'indexes', return a list of attribute 690 names. 691 """ 692 693 all_attrnames = self.optimiser.all_attrnames 694 attrnames = [] 695 for i in indexes: 696 if i is None: 697 attrnames.append(None) 698 else: 699 attrnames.append(all_attrnames[i]) 700 return attrnames 701 702 def get_static_attributes(self, kind, name, attrnames): 703 704 """ 705 Return a mapping of attribute names to paths for attributes belonging 706 to objects of the given 'kind' (being "<class>" or "<module>") with 707 the given 'name' and supporting the given 'attrnames'. 708 """ 709 710 attrs = {} 711 712 for attrname in attrnames: 713 if attrname is None: 714 continue 715 if kind == "<class>": 716 path = self.importer.all_class_attrs[name][attrname] 717 elif kind == "<module>": 718 path = "%s.%s" % (name, attrname) 719 else: 720 continue 721 722 # The module may be hidden. 723 724 attr = self.importer.get_object(path) 725 if not attr: 726 module = self.importer.hidden.get(path) 727 if module: 728 attr = Reference(module.name, "<module>") 729 attrs[attrname] = attr 730 731 return attrs 732 733 def get_instance_attributes(self, name, attrnames): 734 735 """ 736 Return a mapping of attribute names to references for attributes 737 belonging to instances of the class with the given 'name', where the 738 given 'attrnames' are supported. 739 """ 740 741 consts = self.importer.all_instance_attr_constants[name] 742 attrs = {} 743 for attrname in attrnames: 744 if attrname is None: 745 continue 746 const = consts.get(attrname) 747 attrs[attrname] = const or Reference("<var>", "%s.%s" % (name, attrname)) 748 return attrs 749 750 def populate_table(self, path, table): 751 752 """ 753 Traverse the attributes in the determined order for the structure having 754 the given 'path', adding entries to the attribute 'table'. 755 """ 756 757 for attrname in self.optimiser.structures[path]: 758 759 # Handle gaps in the structure. 760 761 if attrname is None: 762 table.append("0") 763 else: 764 table.append(encode_symbol("code", attrname)) 765 766 def populate_parameter_table(self, parameters, table): 767 768 """ 769 Traverse the 'parameters' in the determined order, adding entries to the 770 attribute 'table'. 771 """ 772 773 for value in parameters: 774 775 # Handle gaps in the structure. 776 777 if value is None: 778 table.append(("0", "0")) 779 else: 780 name, pos = value 781 table.append((encode_symbol("pcode", name), pos)) 782 783 def populate_function(self, path, function_instance_attrs, unbound=False): 784 785 """ 786 Populate a structure for the function with the given 'path'. The given 787 'attrs' provide the instance attributes, and if 'unbound' is set to a 788 true value, an unbound method structure is produced (as opposed to a 789 callable bound method structure). 790 """ 791 792 structure = [] 793 self.populate_structure(Reference("<function>", path), function_instance_attrs, "<instance>", structure, unbound) 794 795 # Append default members. 796 797 self.append_defaults(path, structure) 798 return structure 799 800 def populate_structure(self, ref, attrs, kind, structure, unbound=False): 801 802 """ 803 Traverse the attributes in the determined order for the structure having 804 the given 'ref' whose members are provided by the 'attrs' mapping, in a 805 structure of the given 'kind', adding entries to the object 'structure'. 806 If 'unbound' is set to a true value, an unbound method function pointer 807 will be employed, with a reference to the bound method incorporated into 808 the special __fn__ attribute. 809 """ 810 811 # Populate function instance structures for functions. 812 813 if ref.has_kind("<function>"): 814 origin = self.function_type 815 structure_ref = Reference("<instance>", self.function_type) 816 817 # Otherwise, just populate the appropriate structures. 818 819 else: 820 origin = ref.get_origin() 821 structure_ref = ref 822 823 for attrname in self.optimiser.structures[structure_ref]: 824 825 # Handle gaps in the structure. 826 827 if attrname is None: 828 structure.append("{0, 0}") 829 830 # Handle non-constant and constant members. 831 832 else: 833 attr = attrs[attrname] 834 835 # Special function pointer member. 836 837 if attrname == "__fn__": 838 839 # Provide bound method references and the unbound function 840 # pointer if populating methods in a class. 841 842 bound_attr = None 843 844 # Classes offer instantiators. 845 846 if kind == "<class>": 847 attr = encode_instantiator_pointer(attr) 848 849 # Methods offers references to bound versions and an unbound 850 # method function. 851 852 elif unbound: 853 bound_attr = encode_bound_reference(attr) 854 attr = "__unbound_method" 855 856 # Other functions just offer function pointers. 857 858 else: 859 attr = encode_function_pointer(attr) 860 861 structure.append("{%s, .fn=%s}" % (bound_attr and ".b=&%s" % bound_attr or "0", attr)) 862 continue 863 864 # Special argument specification member. 865 866 elif attrname == "__args__": 867 signature = self.get_signature_for_callable(ref.get_origin()) 868 ptable = encode_tablename("<function>", signature) 869 870 structure.append("{.min=%s, .ptable=&%s}" % (attr, ptable)) 871 continue 872 873 # Special internal data member. 874 875 elif attrname == "__data__": 876 structure.append("{0, .%s=%s}" % (encode_literal_constant_member(attr), 877 encode_literal_constant_value(attr))) 878 continue 879 880 # Special internal key member. 881 882 elif attrname == "__key__": 883 structure.append("{.code=%s, .pos=%s}" % (attr and encode_symbol("code", attr) or "0", 884 attr and encode_symbol("pos", attr) or "0")) 885 continue 886 887 # Special cases. 888 889 elif attrname in ("__file__", "__fname__", "__mname__", "__name__"): 890 path = ref.get_origin() 891 892 if attrname == "__file__": 893 module = self.importer.get_module(path) 894 value = module.filename 895 else: 896 value = path 897 898 local_number = self.importer.all_constants[path][value] 899 constant_name = "$c%d" % local_number 900 attr_path = "%s.%s" % (path, constant_name) 901 constant_number = self.optimiser.constant_numbers[attr_path] 902 constant_value = "__const%d" % constant_number 903 structure.append("%s /* %s */" % (constant_value, attrname)) 904 continue 905 906 # Special class relationship attributes. 907 908 elif is_type_attribute(attrname): 909 structure.append("{0, &%s}" % encode_path(decode_type_attribute(attrname))) 910 continue 911 912 structure.append(self.encode_member(origin, attrname, attr, kind)) 913 914 def encode_member(self, path, name, ref, structure_type): 915 916 """ 917 Encode within the structure provided by 'path', the member whose 'name' 918 provides 'ref', within the given 'structure_type'. 919 """ 920 921 kind = ref.get_kind() 922 origin = ref.get_origin() 923 924 # References to constant literals. 925 926 if kind == "<instance>" and ref.is_constant_alias(): 927 alias = ref.get_name() 928 929 # Obtain a constant value directly assigned to the attribute. 930 931 if self.optimiser.constant_numbers.has_key(alias): 932 constant_number = self.optimiser.constant_numbers[alias] 933 constant_value = "__const%d" % constant_number 934 return "%s /* %s */" % (constant_value, name) 935 936 # Predefined constant references. 937 938 if (path, name) in self.predefined_constant_members: 939 attr_path = encode_predefined_reference("%s.%s" % (path, name)) 940 return "{&%s, &%s} /* %s */" % (attr_path, attr_path, name) 941 942 # General undetermined members. 943 944 if kind in ("<var>", "<instance>"): 945 return "{0, 0} /* %s */" % name 946 947 # Set the context depending on the kind of attribute. 948 # For methods: {&<parent>, &<attr>} 949 # For other attributes: {&<attr>, &<attr>} 950 951 else: 952 if kind == "<function>" and structure_type == "<class>": 953 parent = origin.rsplit(".", 1)[0] 954 context = "&%s" % encode_path(parent) 955 elif kind == "<instance>": 956 context = "&%s" % encode_path(origin) 957 else: 958 context = "0" 959 return "{%s, &%s}" % (context, encode_path(origin)) 960 961 def append_defaults(self, path, structure): 962 963 """ 964 For the given 'path', append default parameter members to the given 965 'structure'. 966 """ 967 968 for name, default in self.importer.function_defaults.get(path): 969 structure.append(self.encode_member(path, name, default, "<instance>")) 970 971 def write_instantiator(self, f_code, f_signatures, path, init_ref): 972 973 """ 974 Write an instantiator to 'f_code', with a signature to 'f_signatures', 975 for instances of the class with the given 'path', with 'init_ref' as the 976 initialiser function reference. 977 978 NOTE: This also needs to initialise any __fn__ and __args__ members 979 NOTE: where __call__ is provided by the class. 980 """ 981 982 parameters = self.importer.function_parameters[init_ref.get_origin()] 983 984 print >>f_code, """\ 985 __attr %s(__attr __args[]) 986 { 987 /* Allocate the structure. */ 988 __args[0] = __new(&%s, &%s, sizeof(%s)); 989 990 /* Call the initialiser. */ 991 %s(__args); 992 993 /* Return the allocated object details. */ 994 return __args[0]; 995 } 996 """ % ( 997 encode_instantiator_pointer(path), 998 encode_tablename("<instance>", path), 999 encode_path(path), 1000 encode_symbol("obj", path), 1001 encode_function_pointer(init_ref.get_origin()) 1002 ) 1003 1004 print >>f_signatures, "#define __HAVE_%s" % encode_path(path) 1005 print >>f_signatures, "__attr %s(__attr[]);" % encode_instantiator_pointer(path) 1006 1007 # Write additional literal instantiators. These do not call the 1008 # initialisers but instead populate the structures directly. 1009 1010 if path in self.literal_instantiator_types: 1011 if path in self.literal_mapping_types: 1012 style = "mapping" 1013 else: 1014 style = "sequence" 1015 1016 print >>f_code, """\ 1017 __attr %s(__attr __args[], unsigned int number) 1018 { 1019 /* Allocate the structure. */ 1020 __args[0] = __new(&%s, &%s, sizeof(%s)); 1021 1022 /* Allocate a structure for the data and set it on the __data__ attribute. */ 1023 %s(__args, number); 1024 1025 /* Return the allocated object details. */ 1026 return __args[0]; 1027 } 1028 """ % ( 1029 encode_literal_instantiator(path), 1030 encode_tablename("<instance>", path), 1031 encode_path(path), 1032 encode_symbol("obj", path), 1033 encode_literal_data_initialiser(style) 1034 ) 1035 1036 print >>f_signatures, "__attr %s(__attr[], unsigned int);" % encode_literal_instantiator(path) 1037 1038 def write_main_program(self, f_code, f_signatures): 1039 1040 """ 1041 Write the main program to 'f_code', invoking the program's modules. Also 1042 write declarations for module main functions to 'f_signatures'. 1043 """ 1044 1045 print >>f_code, """\ 1046 int main(int argc, char *argv[]) 1047 { 1048 __exc __tmp_exc; 1049 1050 __Try 1051 {""" 1052 1053 for name in self.importer.order_modules(): 1054 function_name = "__main_%s" % encode_path(name) 1055 print >>f_signatures, "void %s();" % function_name 1056 1057 # Omit the native modules. 1058 1059 parts = name.split(".") 1060 1061 if parts[0] != "native": 1062 print >>f_code, """\ 1063 %s();""" % function_name 1064 1065 print >>f_code, """\ 1066 return 0; 1067 } 1068 __Catch(__tmp_exc) 1069 { 1070 fprintf(stderr, "Program terminated due to exception: %%s.\\n", 1071 __load_via_object( 1072 %s((__attr[]) {{0, 0}, __tmp_exc.arg}).value, 1073 %s).strvalue); 1074 return 1; 1075 } 1076 } 1077 """ % ( 1078 encode_function_pointer("__builtins__.str.str"), 1079 encode_symbol("pos", "__data__") 1080 ) 1081 1082 # vim: tabstop=4 expandtab shiftwidth=4