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_size, 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 constant, n in self.optimiser.constants.items(): 345 self.make_literal_constant(f_decls, f_defs, n, constant) 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, constant): 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 'constant'. 442 """ 443 444 value, value_type = constant 445 446 const_path = encode_literal_constant(n) 447 structure_name = encode_literal_reference(n) 448 449 ref = Reference("<instance>", value_type) 450 self.make_constant(f_decls, f_defs, ref, const_path, structure_name, value) 451 452 def make_predefined_constant(self, f_decls, f_defs, path, name): 453 454 """ 455 Write predefined constant details to 'f_decls' (to declare a structure) 456 and to 'f_defs' (to define the contents) for the constant located in 457 'path' with the given 'name'. 458 """ 459 460 # Determine the details of the constant. 461 462 attr_path = "%s.%s" % (path, name) 463 structure_name = encode_predefined_reference(attr_path) 464 ref = self.importer.get_object(attr_path) 465 466 self.make_constant(f_decls, f_defs, ref, attr_path, structure_name) 467 468 def make_constant(self, f_decls, f_defs, ref, const_path, structure_name, data=None): 469 470 """ 471 Write constant details to 'f_decls' (to declare a structure) and to 472 'f_defs' (to define the contents) for the constant described by 'ref' 473 having the given 'path' and 'structure_name' (for the constant structure 474 itself). 475 """ 476 477 # Obtain the attributes. 478 479 cls = ref.get_origin() 480 indexes = self.optimiser.attr_table[ref] 481 attrnames = self.get_attribute_names(indexes) 482 attrs = self.get_instance_attributes(cls, attrnames) 483 484 # Set the data, if provided. 485 486 if data is not None: 487 attrs["__data__"] = data 488 489 # Also set a key for dynamic attribute lookup, if a string. 490 491 if attrs.has_key("__key__"): 492 if data in self.optimiser.all_attrnames: 493 attrs["__key__"] = data 494 else: 495 attrs["__key__"] = None 496 497 # Define the structure details. An object is created for the constant, 498 # but an attribute is provided, referring to the object, for access to 499 # the constant in the program. 500 501 structure = [] 502 table_name = encode_tablename("<instance>", cls) 503 self.populate_structure(ref, attrs, ref.get_kind(), structure) 504 self.write_structure(f_decls, f_defs, structure_name, table_name, structure) 505 506 # Define a macro for the constant. 507 508 attr_name = encode_path(const_path) 509 print >>f_decls, "#define %s ((__attr) {&%s, &%s})" % (attr_name, structure_name, structure_name) 510 511 def make_parameter_table(self, f_decls, f_defs, parameters): 512 513 """ 514 Write parameter table details to 'f_decls' (to declare a table) and to 515 'f_defs' (to define the contents) for the given 'parameters'. 516 """ 517 518 # Use a signature for the table name instead of a separate name for each 519 # function. 520 521 signature = self.get_parameter_signature(parameters) 522 table_name = encode_tablename("<function>", signature) 523 structure_size = encode_size("pmax", signature) 524 525 table = [] 526 self.populate_parameter_table(parameters, table) 527 self.write_parameter_table(f_decls, f_defs, table_name, structure_size, table) 528 529 def get_parameter_signature(self, parameters): 530 531 "Return a signature for the given 'parameters'." 532 533 l = [] 534 for parameter in parameters: 535 if parameter is None: 536 l.append("") 537 else: 538 name, pos = parameter 539 l.append("%s_%s" % (name, pos)) 540 return l and "__".join(l) or "__void" 541 542 def get_signature_for_callable(self, path): 543 544 "Return the signature for the callable with the given 'path'." 545 546 function_path = self.callables[path] 547 parameters = self.optimiser.parameters[function_path] 548 return self.get_parameter_signature(parameters) 549 550 def write_size_constants(self, f_consts, size_prefix, sizes, padding): 551 552 """ 553 Write size constants to 'f_consts' for the given 'size_prefix', using 554 the 'sizes' dictionary to populate the definition, adding the given 555 'padding' to the basic sizes. 556 """ 557 558 print >>f_consts, "enum %s {" % encode_size(size_prefix) 559 first = True 560 for path, size in sizes.items(): 561 if not first: 562 print >>f_consts, "," 563 else: 564 first = False 565 f_consts.write(" %s = %d" % (encode_size(size_prefix, path), size + padding)) 566 print >>f_consts, "\n };" 567 568 def write_code_constants(self, f_consts, attrnames, locations, code_prefix, pos_prefix): 569 570 """ 571 Write code constants to 'f_consts' for the given 'attrnames' and 572 attribute 'locations'. 573 """ 574 575 print >>f_consts, "enum %s {" % encode_symbol(code_prefix) 576 first = True 577 for i, attrname in enumerate(attrnames): 578 if not first: 579 print >>f_consts, "," 580 else: 581 first = False 582 f_consts.write(" %s = %d" % (encode_symbol(code_prefix, attrname), i)) 583 print >>f_consts, "\n };" 584 585 print >>f_consts, "enum %s {" % encode_symbol(pos_prefix) 586 first = True 587 for i, attrnames in enumerate(locations): 588 for attrname in attrnames: 589 if not first: 590 print >>f_consts, "," 591 else: 592 first = False 593 f_consts.write(" %s = %d" % (encode_symbol(pos_prefix, attrname), i)) 594 print >>f_consts, "\n };" 595 596 def write_table(self, f_decls, f_defs, table_name, structure_size, table): 597 598 """ 599 Write the declarations to 'f_decls' and definitions to 'f_defs' for 600 the object having the given 'table_name' and the given 'structure_size', 601 with 'table' details used to populate the definition. 602 """ 603 604 print >>f_decls, "extern const __table %s;\n" % table_name 605 606 # Write the corresponding definition. 607 608 print >>f_defs, "const __table %s = {\n %s,\n {\n %s\n }\n };\n" % ( 609 table_name, structure_size, 610 ",\n ".join(table)) 611 612 def write_parameter_table(self, f_decls, f_defs, table_name, structure_size, table): 613 614 """ 615 Write the declarations to 'f_decls' and definitions to 'f_defs' for 616 the object having the given 'table_name' and the given 'structure_size', 617 with 'table' details used to populate the definition. 618 """ 619 620 print >>f_decls, "extern const __ptable %s;\n" % table_name 621 622 # Write the corresponding definition. 623 624 print >>f_defs, "const __ptable %s = {\n %s,\n {\n %s\n }\n };\n" % ( 625 table_name, structure_size, 626 ",\n ".join([("{%s, %s}" % t) for t in table])) 627 628 def write_instance_structure(self, f_decls, path, structure_size): 629 630 """ 631 Write a declaration to 'f_decls' for the object having the given 'path' 632 and the given 'structure_size'. 633 """ 634 635 # Write an instance-specific type definition for instances of classes. 636 # See: templates/types.h 637 638 print >>f_decls, """\ 639 typedef struct { 640 const __table * table; 641 unsigned int pos; 642 __attr attrs[%s]; 643 } %s; 644 """ % (structure_size, encode_symbol("obj", path)) 645 646 def write_structure(self, f_decls, f_defs, structure_name, table_name, structure, path=None): 647 648 """ 649 Write the declarations to 'f_decls' and definitions to 'f_defs' for 650 the object having the given 'structure_name', the given 'table_name', 651 and the given 'structure' details used to populate the definition. 652 """ 653 654 if f_decls: 655 print >>f_decls, "extern __obj %s;\n" % encode_path(structure_name) 656 657 is_class = path and self.importer.get_object(path).has_kind("<class>") 658 pos = is_class and encode_symbol("pos", encode_type_attribute(path)) or "0" 659 660 print >>f_defs, """\ 661 __obj %s = { 662 &%s, 663 %s, 664 { 665 %s 666 }}; 667 """ % ( 668 encode_path(structure_name), table_name, pos, 669 ",\n ".join(structure)) 670 671 def get_argument_limits(self, path): 672 673 """ 674 Return the argument minimum and maximum for the callable at 'path', 675 adding an argument position for a universal context. 676 """ 677 678 parameters = self.importer.function_parameters[path] 679 defaults = self.importer.function_defaults.get(path) 680 num_parameters = len(parameters) + 1 681 return num_parameters - (defaults and len(defaults) or 0), num_parameters 682 683 def get_attribute_names(self, indexes): 684 685 """ 686 Given a list of attribute table 'indexes', return a list of attribute 687 names. 688 """ 689 690 all_attrnames = self.optimiser.all_attrnames 691 attrnames = [] 692 for i in indexes: 693 if i is None: 694 attrnames.append(None) 695 else: 696 attrnames.append(all_attrnames[i]) 697 return attrnames 698 699 def get_static_attributes(self, kind, name, attrnames): 700 701 """ 702 Return a mapping of attribute names to paths for attributes belonging 703 to objects of the given 'kind' (being "<class>" or "<module>") with 704 the given 'name' and supporting the given 'attrnames'. 705 """ 706 707 attrs = {} 708 709 for attrname in attrnames: 710 if attrname is None: 711 continue 712 if kind == "<class>": 713 path = self.importer.all_class_attrs[name][attrname] 714 elif kind == "<module>": 715 path = "%s.%s" % (name, attrname) 716 else: 717 continue 718 719 # The module may be hidden. 720 721 attr = self.importer.get_object(path) 722 if not attr: 723 module = self.importer.hidden.get(path) 724 if module: 725 attr = Reference(module.name, "<module>") 726 attrs[attrname] = attr 727 728 return attrs 729 730 def get_instance_attributes(self, name, attrnames): 731 732 """ 733 Return a mapping of attribute names to references for attributes 734 belonging to instances of the class with the given 'name', where the 735 given 'attrnames' are supported. 736 """ 737 738 consts = self.importer.all_instance_attr_constants[name] 739 attrs = {} 740 for attrname in attrnames: 741 if attrname is None: 742 continue 743 const = consts.get(attrname) 744 attrs[attrname] = const or Reference("<var>", "%s.%s" % (name, attrname)) 745 return attrs 746 747 def populate_table(self, path, table): 748 749 """ 750 Traverse the attributes in the determined order for the structure having 751 the given 'path', adding entries to the attribute 'table'. 752 """ 753 754 for attrname in self.optimiser.structures[path]: 755 756 # Handle gaps in the structure. 757 758 if attrname is None: 759 table.append("0") 760 else: 761 table.append(encode_symbol("code", attrname)) 762 763 def populate_parameter_table(self, parameters, table): 764 765 """ 766 Traverse the 'parameters' in the determined order, adding entries to the 767 attribute 'table'. 768 """ 769 770 for value in parameters: 771 772 # Handle gaps in the structure. 773 774 if value is None: 775 table.append(("0", "0")) 776 else: 777 name, pos = value 778 table.append((encode_symbol("pcode", name), pos)) 779 780 def populate_function(self, path, function_instance_attrs, unbound=False): 781 782 """ 783 Populate a structure for the function with the given 'path'. The given 784 'attrs' provide the instance attributes, and if 'unbound' is set to a 785 true value, an unbound method structure is produced (as opposed to a 786 callable bound method structure). 787 """ 788 789 structure = [] 790 self.populate_structure(Reference("<function>", path), function_instance_attrs, "<instance>", structure, unbound) 791 792 # Append default members. 793 794 self.append_defaults(path, structure) 795 return structure 796 797 def populate_structure(self, ref, attrs, kind, structure, unbound=False): 798 799 """ 800 Traverse the attributes in the determined order for the structure having 801 the given 'ref' whose members are provided by the 'attrs' mapping, in a 802 structure of the given 'kind', adding entries to the object 'structure'. 803 If 'unbound' is set to a true value, an unbound method function pointer 804 will be employed, with a reference to the bound method incorporated into 805 the special __fn__ attribute. 806 """ 807 808 # Populate function instance structures for functions. 809 810 if ref.has_kind("<function>"): 811 origin = self.function_type 812 structure_ref = Reference("<instance>", self.function_type) 813 814 # Otherwise, just populate the appropriate structures. 815 816 else: 817 origin = ref.get_origin() 818 structure_ref = ref 819 820 for attrname in self.optimiser.structures[structure_ref]: 821 822 # Handle gaps in the structure. 823 824 if attrname is None: 825 structure.append("{0, 0}") 826 827 # Handle non-constant and constant members. 828 829 else: 830 attr = attrs[attrname] 831 832 # Special function pointer member. 833 834 if attrname == "__fn__": 835 836 # Provide bound method references and the unbound function 837 # pointer if populating methods in a class. 838 839 bound_attr = None 840 841 # Classes offer instantiators. 842 843 if kind == "<class>": 844 attr = encode_instantiator_pointer(attr) 845 846 # Methods offers references to bound versions and an unbound 847 # method function. 848 849 elif unbound: 850 bound_attr = encode_bound_reference(attr) 851 attr = "__unbound_method" 852 853 # Other functions just offer function pointers. 854 855 else: 856 attr = encode_function_pointer(attr) 857 858 structure.append("{%s, .fn=%s}" % (bound_attr and ".b=&%s" % bound_attr or "0", attr)) 859 continue 860 861 # Special argument specification member. 862 863 elif attrname == "__args__": 864 signature = self.get_signature_for_callable(ref.get_origin()) 865 ptable = encode_tablename("<function>", signature) 866 867 structure.append("{.min=%s, .ptable=&%s}" % (attr, ptable)) 868 continue 869 870 # Special internal data member. 871 872 elif attrname == "__data__": 873 structure.append("{.size=%d, .%s=%s}" % ( 874 encode_literal_constant_size(attr), 875 encode_literal_constant_member(attr), 876 encode_literal_constant_value(attr))) 877 continue 878 879 # Special internal key member. 880 881 elif attrname == "__key__": 882 structure.append("{.code=%s, .pos=%s}" % (attr and encode_symbol("code", attr) or "0", 883 attr and encode_symbol("pos", attr) or "0")) 884 continue 885 886 # Special cases. 887 888 elif attrname in ("__file__", "__fname__", "__mname__", "__name__"): 889 path = ref.get_origin() 890 value_type = self.string_type 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, value_type)] 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