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