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