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