Lichen

generator.py

755:5fd3c0524204
2017-03-21 Paul Boddie Added support for an integer cache containing specific pre-allocated objects. integer-cache
     1 #!/usr/bin/env python     2      3 """     4 Generate C code from object layouts and other deduced information.     5      6 Copyright (C) 2015, 2016, 2017 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, copy, indent    23 from encoders import encode_code, \    24                      encode_function_pointer, \    25                      encode_instantiator_pointer, \    26                      encode_literal_constant, encode_literal_constant_member, \    27                      encode_literal_constant_size, encode_literal_constant_value, \    28                      encode_literal_data_initialiser, \    29                      encode_literal_instantiator, encode_literal_reference, \    30                      encode_path, encode_pcode, encode_pos, encode_ppos, \    31                      encode_predefined_reference, encode_size, \    32                      encode_symbol, encode_tablename, \    33                      encode_type_attribute, decode_type_attribute, \    34                      is_type_attribute    35 from os import listdir, mkdir, remove    36 from os.path import exists, isdir, join, split, splitext    37 from referencing import Reference    38     39 class Generator(CommonOutput):    40     41     "A code generator."    42     43     # NOTE: These must be synchronised with the library.    44     45     function_type = "__builtins__.core.function"    46     none_type = "__builtins__.none.NoneType"    47     int_type = "__builtins__.int.int"    48     string_type = "__builtins__.str.string"    49     type_type = "__builtins__.core.type"    50     unicode_type = "__builtins__.unicode.utf8string"    51     52     none_value = "__builtins__.none.None"    53     54     predefined_constant_members = (    55         ("__builtins__.boolean", "False"),    56         ("__builtins__.boolean", "True"),    57         ("__builtins__.none", "None"),    58         ("__builtins__.notimplemented", "NotImplemented"),    59         )    60     61     literal_mapping_types = (    62         "__builtins__.dict.dict",    63         )    64     65     literal_sequence_types = (    66         "__builtins__.list.list",    67         "__builtins__.tuple.tuple",    68         )    69     70     literal_instantiator_types = literal_mapping_types + literal_sequence_types    71     72     def __init__(self, importer, optimiser, output):    73     74         """    75         Initialise the generator with the given 'importer', 'optimiser' and    76         'output' directory.    77         """    78     79         self.importer = importer    80         self.optimiser = optimiser    81         self.output = output    82     83         # The special instance indicator.    84     85         self.instancepos = self.optimiser.attr_locations["__class__"]    86     87     def to_output(self, reset=False, debug=False, gc_sections=False):    88     89         "Write the generated code."    90     91         self.check_output("debug=%r gc_sections=%r" % (debug, gc_sections))    92         self.write_structures()    93         self.write_scripts(debug, gc_sections)    94         self.copy_templates(reset)    95     96     def copy_templates(self, reset=False):    97     98         "Copy template files to the generated output directory."    99    100         templates = join(split(__file__)[0], "templates")   101    102         only_if_newer = not reset   103    104         for filename in listdir(templates):   105             target = self.output   106             pathname = join(templates, filename)   107    108             # Copy files into the target directory.   109    110             if not isdir(pathname):   111                 copy(pathname, target, only_if_newer)   112    113             # Copy directories (such as the native code directory).   114    115             else:   116                 target = join(self.output, filename)   117    118                 if not exists(target):   119                     mkdir(target)   120    121                 existing = listdir(target)   122                 needed = listdir(pathname)   123    124                 # Determine which files are superfluous by comparing their   125                 # basenames (without extensions) to those of the needed   126                 # filenames. This should preserve object files for needed source   127                 # files, only discarding completely superfluous files from the   128                 # target directory.   129    130                 needed_basenames = set()   131                 for filename in needed:   132                     needed_basenames.add(splitext(filename)[0])   133    134                 superfluous = []   135                 for filename in existing:   136                     if splitext(filename)[0] not in needed_basenames:   137                         superfluous.append(filename)   138    139                 # Copy needed files.   140    141                 for filename in needed:   142                     copy(join(pathname, filename), target, only_if_newer)   143    144                 # Remove superfluous files.   145    146                 for filename in superfluous:   147                     remove(join(target, filename))   148    149     def write_structures(self):   150    151         "Write structures used by the program."   152    153         f_consts = open(join(self.output, "progconsts.h"), "w")   154         f_defs = open(join(self.output, "progtypes.c"), "w")   155         f_decls = open(join(self.output, "progtypes.h"), "w")   156         f_signatures = open(join(self.output, "main.h"), "w")   157         f_code = open(join(self.output, "main.c"), "w")   158    159         try:   160             # Output boilerplate.   161    162             print >>f_consts, """\   163 #ifndef __PROGCONSTS_H__   164 #define __PROGCONSTS_H__   165    166 #include "types.h"   167 """   168             print >>f_decls, """\   169 #ifndef __PROGTYPES_H__   170 #define __PROGTYPES_H__   171    172 #include "progconsts.h"   173 #include "types.h"   174 """   175             print >>f_defs, """\   176 #include "progtypes.h"   177 #include "progops.h"   178 #include "main.h"   179 """   180             print >>f_signatures, """\   181 #ifndef __MAIN_H__   182 #define __MAIN_H__   183    184 #include "types.h"   185 """   186             print >>f_code, """\   187 #include <string.h>   188 #include <stdio.h>   189 #include "gc.h"   190 #include "types.h"   191 #include "exceptions.h"   192 #include "ops.h"   193 #include "progconsts.h"   194 #include "progtypes.h"   195 #include "main.h"   196 #include "progops.h"   197 """   198    199             # Generate table and structure data.   200    201             function_instance_attrs = None   202             objects = self.optimiser.attr_table.items()   203             objects.sort()   204    205             self.callables = {}   206    207             for ref, indexes in objects:   208                 attrnames = self.get_attribute_names(indexes)   209    210                 kind = ref.get_kind()   211                 path = ref.get_origin()   212                 table_name = encode_tablename(kind, path)   213                 structure_size = encode_size(kind, path)   214    215                 # Generate structures for classes and modules.   216    217                 if kind != "<instance>":   218                     structure = []   219                     attrs = self.get_static_attributes(kind, path, attrnames)   220    221                     # Set a special instantiator on the class.   222    223                     if kind == "<class>":   224    225                         # Write instantiator declarations based on the   226                         # applicable initialiser.   227    228                         init_ref = attrs["__init__"]   229    230                         # Write instantiator definitions.   231    232                         self.write_instantiator(f_code, f_signatures, path, init_ref)   233    234                         # Record the callable for parameter table generation.   235    236                         self.callables[path] = init_ref.get_origin()   237    238                         # Define special attributes.   239    240                         attrs["__fn__"] = path   241                         attrs["__args__"] = path   242    243                     self.populate_structure(Reference(kind, path), attrs, kind, structure)   244    245                     if kind == "<class>":   246                         self.write_instance_structure(f_decls, path, structure_size)   247    248                     self.write_structure(f_decls, f_defs, path, table_name, structure,   249                         kind == "<class>" and path)   250    251                 # Record function instance details for function generation below.   252    253                 else:   254                     attrs = self.get_instance_attributes(path, attrnames)   255                     if path == self.function_type:   256                         function_instance_attrs = attrs   257    258                         # Record the callable for parameter table generation.   259    260                         self.callables[path] = path   261    262                 # Write a table for all objects.   263    264                 table = []   265                 self.populate_table(Reference(kind, path), table)   266                 self.write_table(f_decls, f_defs, table_name, structure_size, table)   267    268             # Generate function instances.   269    270             functions = self.importer.function_parameters.keys()   271             functions.sort()   272             extra_function_instances = []   273    274             for path in functions:   275    276                 # Instantiators are generated above.   277    278                 if self.importer.classes.has_key(path) or not self.importer.get_object(path):   279                     continue   280    281                 # Record the callable for parameter table generation.   282    283                 self.callables[path] = path   284    285                 # Define the structure details.   286    287                 cls = self.function_type   288                 table_name = encode_tablename("<instance>", cls)   289                 structure_size = encode_size("<instance>", path)   290    291                 # Set a special callable attribute on the instance.   292    293                 function_instance_attrs["__fn__"] = path   294                 function_instance_attrs["__args__"] = path   295    296                 structure = self.populate_function(path, function_instance_attrs)   297                 self.write_structure(f_decls, f_defs, path, table_name, structure)   298    299                 # Functions with defaults need to declare instance structures.   300    301                 if self.importer.function_defaults.get(path):   302                     self.write_instance_structure(f_decls, path, structure_size)   303                     extra_function_instances.append(path)   304    305                 # Write function declarations.   306                 # Signature: __attr <name>(__attr[]);   307    308                 print >>f_signatures, "__attr %s(__attr args[]);" % encode_function_pointer(path)   309    310             # Generate parameter table size data.   311    312             min_parameters = {}   313             max_parameters = {}   314             size_parameters = {}   315    316             # Consolidate parameter tables for instantiators and functions.   317    318             parameter_tables = set()   319    320             for path, function_path in self.callables.items():   321                 argmin, argmax = self.get_argument_limits(function_path)   322    323                 # Obtain the parameter table members.   324    325                 parameters = self.optimiser.parameters[function_path]   326                 if not parameters:   327                     parameters = ()   328                 else:   329                     parameters = tuple(parameters)   330    331                 # Define each table in terms of the members and the minimum   332                 # number of arguments.   333    334                 parameter_tables.add((argmin, parameters))   335                 signature = self.get_parameter_signature(argmin, parameters)   336    337                 # Record the minimum number of arguments, the maximum number,   338                 # and the size of the table.   339    340                 min_parameters[signature] = argmin   341                 max_parameters[signature] = argmax   342                 size_parameters[signature] = len(parameters)   343    344             self.write_size_constants(f_consts, "pmin", min_parameters, 0)   345             self.write_size_constants(f_consts, "pmax", max_parameters, 0)   346             self.write_size_constants(f_consts, "psize", size_parameters, 0)   347    348             # Generate parameter tables for distinct function signatures.   349    350             for argmin, parameters in parameter_tables:   351                 self.make_parameter_table(f_decls, f_defs, argmin, parameters)   352    353             # Generate predefined constants.   354    355             for path, name in self.predefined_constant_members:   356                 self.make_predefined_constant(f_decls, f_defs, path, name)   357    358             # Generate literal constants.   359    360             for constant, n in self.optimiser.constants.items():   361                 self.make_literal_constant(f_decls, f_defs, n, constant)   362    363             # Generate pre-allocated objects.   364    365             self.make_preallocated_objects(f_decls, f_defs)   366    367             # Finish the main source file.   368    369             self.write_main_program(f_code, f_signatures)   370    371             # Record size information for certain function instances as well as   372             # for classes, modules and other instances.   373    374             size_tables = {}   375    376             for kind in ["<class>", "<module>", "<instance>"]:   377                 size_tables[kind] = {}   378    379             # Generate structure size data.   380    381             for ref, structure in self.optimiser.structures.items():   382                 size_tables[ref.get_kind()][ref.get_origin()] = len(structure)   383    384             for path in extra_function_instances:   385                 defaults = self.importer.function_defaults[path]   386                 size_tables["<instance>"][path] = size_tables["<instance>"][self.function_type] + len(defaults)   387    388             size_tables = size_tables.items()   389             size_tables.sort()   390    391             for kind, sizes in size_tables:   392                 self.write_size_constants(f_consts, kind, sizes, 0)   393    394             # Generate parameter codes.   395    396             self.write_code_constants(f_consts, self.optimiser.all_paramnames,   397                                       self.optimiser.arg_locations,   398                                       "pcode", "ppos", encode_pcode, encode_ppos)   399    400             # Generate attribute codes.   401    402             self.write_code_constants(f_consts, self.optimiser.all_attrnames,   403                                       self.optimiser.locations,   404                                       "code", "pos", encode_code, encode_pos)   405    406             # Output more boilerplate.   407    408             print >>f_consts, """\   409    410 #endif /* __PROGCONSTS_H__ */"""   411    412             print >>f_decls, """\   413    414 #define __FUNCTION_TYPE %s   415 #define __FUNCTION_INSTANCE_SIZE %s   416 #define __TYPE_CLASS_TYPE %s   417 #define __TYPE_CLASS_POS %s   418 #define __TYPE_CLASS_CODE %s   419    420 #endif /* __PROGTYPES_H__ */""" % (   421     encode_path(self.function_type),   422     encode_size("<instance>", self.function_type),   423     encode_path(self.type_type),   424     encode_pos(encode_type_attribute(self.type_type)),   425     encode_code(encode_type_attribute(self.type_type)),   426     )   427    428             print >>f_signatures, """\   429    430 #endif /* __MAIN_H__ */"""   431    432         finally:   433             f_consts.close()   434             f_defs.close()   435             f_decls.close()   436             f_signatures.close()   437             f_code.close()   438    439     def write_scripts(self, debug, gc_sections):   440    441         "Write scripts used to build the program."   442    443         # Options affect compiling and linking.   444    445         f_options = open(join(self.output, "options.mk"), "w")   446         try:   447             if debug:   448                 print >>f_options, "CFLAGS = -g"   449             else:   450                 print >>f_options, "CFLAGS = -O2"   451    452             if gc_sections:   453                 print >>f_options, "include gc_sections.mk"   454    455         finally:   456             f_options.close()   457    458         # Native and module definitions divide the program modules into native   459         # and generated code.   460    461         f_native = open(join(self.output, "native.mk"), "w")   462         f_modules = open(join(self.output, "modules.mk"), "w")   463         try:   464             # Identify modules used by the program.   465    466             native_modules = [join("native", "common.c")]   467             modules = []   468    469             for name in self.importer.modules.keys():   470                 parts = name.split(".", 1)   471    472                 # Identify source files to be built.   473    474                 if parts[0] == "native":   475                     native_modules.append(join("native", "%s.c" % parts[1]))   476                 else:   477                     modules.append(join("src", "%s.c" % name))   478    479             print >>f_native, "SRC =", " ".join(native_modules)   480             print >>f_modules, "SRC +=", " ".join(modules)   481    482         finally:   483             f_native.close()   484             f_modules.close()   485    486         # Instance position configuration uses the position of the ubiquitous   487         # __class__ attribute as a means of indicating that an object is an   488         # instance. Classes employ special identifying attributes that are   489         # positioned elsewhere and thus cannot be in the same location as the   490         # __class__ attribute.   491    492         f_instancepos = open(join(self.output, "instancepos.h"), "w")   493         try:   494             print >>f_instancepos, """\   495 #ifndef __INSTANCEPOS   496 #define __INSTANCEPOS %d   497 #endif   498 """ % self.instancepos   499         finally:   500             f_instancepos.close()   501    502     def make_preallocated_objects(self, f_decls, f_defs):   503    504         """   505         Write pre-allocated object declarations to 'f_decls' and definitions to   506         'f_defs'.   507         """   508    509         ref = Reference("<instance>", self.int_type)   510         table_name = encode_tablename(ref.get_kind(), ref.get_origin())   511         attrs = self.get_instance_attributes_for_reference(ref)   512         specific_instance_type = encode_symbol("inst", self.int_type)   513    514         integers = []   515         integer_cache_size = 256   516         i = 0   517    518         while i < integer_cache_size:   519             attrs["__data__"] = i   520             structure = []   521             self.populate_structure(ref, attrs, ref.get_kind(), structure)   522             integers.append(indent("    ", self.get_structure_definition(table_name, structure)))   523             i += 1   524    525         # Generate an array of specific instances.   526    527         print >>f_defs, """\   528 %s __integer_cache[%d] = {   529 %s   530     };""" % (specific_instance_type, integer_cache_size, ",\n    ".join(integers))   531    532         # Generate a declaration for the specific instance type.   533    534         structure_size = encode_size("<instance>", self.int_type)   535         self.write_instance_structure(f_decls, self.int_type, structure_size, "inst")   536    537         # Generate declarations for the size of the array and the array itself.   538    539         print >>f_decls, """\   540 #define __INTEGER_CACHE_SIZE %d   541 %s __integer_cache[%d];""" % (integer_cache_size, specific_instance_type,   542                               integer_cache_size)   543    544     def make_literal_constant(self, f_decls, f_defs, n, constant):   545    546         """   547         Write literal constant details to 'f_decls' (to declare a structure) and   548         to 'f_defs' (to define the contents) for the constant with the number   549         'n' with the given 'constant'.   550         """   551    552         value, value_type, encoding = constant   553    554         const_path = encode_literal_constant(n)   555         structure_name = encode_literal_reference(n)   556    557         ref = Reference("<instance>", value_type)   558         self.make_constant(f_decls, f_defs, ref, const_path, structure_name, value, encoding)   559    560     def make_predefined_constant(self, f_decls, f_defs, path, name):   561    562         """   563         Write predefined constant details to 'f_decls' (to declare a structure)   564         and to 'f_defs' (to define the contents) for the constant located in   565         'path' with the given 'name'.   566         """   567    568         # Determine the details of the constant.   569    570         attr_path = "%s.%s" % (path, name)   571         structure_name = encode_predefined_reference(attr_path)   572         ref = self.importer.get_object(attr_path)   573    574         self.make_constant(f_decls, f_defs, ref, attr_path, structure_name)   575    576     def make_constant(self, f_decls, f_defs, ref, const_path, structure_name, data=None, encoding=None):   577    578         """   579         Write constant details to 'f_decls' (to declare a structure) and to   580         'f_defs' (to define the contents) for the constant described by 'ref'   581         having the given 'const_path' (used to refer to the structure using an   582         attribute) and 'structure_name' (for the constant structure itself).   583    584         The additional 'data' and 'encoding' are used to describe specific   585         values.   586         """   587    588         # Obtain the attributes.   589    590         cls = ref.get_origin()   591         attrs = self.get_instance_attributes_for_reference(ref)   592    593         # Set the data, if provided.   594    595         if data is not None:   596             attrs["__data__"] = data   597    598             # Also set a key for dynamic attribute lookup, if a string.   599    600             if attrs.has_key("__key__"):   601                 if data in self.optimiser.all_attrnames:   602                     attrs["__key__"] = data   603                 else:   604                     attrs["__key__"] = None   605    606             # Initialise the size, if a string.   607    608             if attrs.has_key("__size__"):   609                 attrs["__size__"] = len(data)   610    611         # Define Unicode constant encoding details.   612    613         if cls == self.unicode_type:   614    615             # Reference the encoding's own constant value.   616    617             if encoding:   618                 n = self.optimiser.constants[(encoding, self.string_type, None)]   619    620                 # Employ a special alias that will be tested specifically in   621                 # encode_member.   622    623                 encoding_ref = Reference("<instance>", self.string_type, "$c%s" % n)   624    625             # Use None where no encoding was indicated.   626    627             else:   628                 encoding_ref = Reference("<instance>", self.none_type)   629    630             attrs["encoding"] = encoding_ref   631    632         # Define the structure details. An object is created for the constant,   633         # but an attribute is provided, referring to the object, for access to   634         # the constant in the program.   635    636         structure = []   637         table_name = encode_tablename("<instance>", cls)   638         self.populate_structure(ref, attrs, ref.get_kind(), structure)   639         self.write_structure(f_decls, f_defs, structure_name, table_name, structure)   640    641         # Define a macro for the constant.   642    643         if const_path:   644             print >>f_decls, "#define %s __ATTRVALUE(&%s)" % (   645                 encode_path(const_path), structure_name)   646    647     def make_parameter_table(self, f_decls, f_defs, argmin, parameters):   648    649         """   650         Write parameter table details to 'f_decls' (to declare a table) and to   651         'f_defs' (to define the contents) for the given 'argmin' and   652         'parameters'.   653         """   654    655         # Use a signature for the table name instead of a separate name for each   656         # function.   657    658         signature = self.get_parameter_signature(argmin, parameters)   659         table_name = encode_tablename("<function>", signature)   660         min_parameters = encode_size("pmin", signature)   661         max_parameters = encode_size("pmax", signature)   662         structure_size = encode_size("psize", signature)   663    664         table = []   665         self.populate_parameter_table(parameters, table)   666         self.write_parameter_table(f_decls, f_defs, table_name, min_parameters, max_parameters, structure_size, table)   667    668     def get_parameter_signature(self, argmin, parameters):   669    670         "Return a signature for the given 'argmin' and 'parameters'."   671    672         l = [str(argmin)]   673         for parameter in parameters:   674             if parameter is None:   675                 l.append("")   676             else:   677                 name, pos = parameter   678                 l.append("%s_%s" % (name, pos))   679         return l and "__".join(l) or "__void"   680    681     def get_signature_for_callable(self, path):   682    683         "Return the signature for the callable with the given 'path'."   684    685         function_path = self.callables[path]   686         argmin, argmax = self.get_argument_limits(function_path)   687         parameters = self.optimiser.parameters[function_path]   688         return self.get_parameter_signature(argmin, parameters)   689    690     def write_size_constants(self, f_consts, size_prefix, sizes, padding):   691    692         """   693         Write size constants to 'f_consts' for the given 'size_prefix', using   694         the 'sizes' dictionary to populate the definition, adding the given   695         'padding' to the basic sizes.   696         """   697    698         print >>f_consts, "enum %s {" % encode_size(size_prefix)   699         first = True   700         for path, size in sizes.items():   701             if not first:   702                 print >>f_consts, ","   703             else:   704                 first = False   705             f_consts.write("    %s = %d" % (encode_size(size_prefix, path), size + padding))   706         print >>f_consts, "\n    };"   707    708     def write_code_constants(self, f_consts, attrnames, locations, code_prefix,   709                              pos_prefix, code_encoder, pos_encoder):   710    711         """   712         Write code constants to 'f_consts' for the given 'attrnames' and   713         attribute 'locations'.   714         """   715    716         print >>f_consts, "enum %s {" % encode_symbol(code_prefix)   717         first = True   718         for i, attrname in enumerate(attrnames):   719             if not first:   720                 print >>f_consts, ","   721             else:   722                 first = False   723             f_consts.write("    %s = %d" % (code_encoder(attrname), i))   724         print >>f_consts, "\n    };"   725    726         print >>f_consts, "enum %s {" % encode_symbol(pos_prefix)   727         first = True   728         for i, attrnames in enumerate(locations):   729             for attrname in attrnames:   730                 if not first:   731                     print >>f_consts, ","   732                 else:   733                     first = False   734                 f_consts.write("    %s = %d" % (pos_encoder(attrname), i))   735         print >>f_consts, "\n    };"   736    737     def write_table(self, f_decls, f_defs, table_name, structure_size, table):   738    739         """   740         Write the declarations to 'f_decls' and definitions to 'f_defs' for   741         the object having the given 'table_name' and the given 'structure_size',   742         with 'table' details used to populate the definition.   743         """   744    745         print >>f_decls, "extern const __table %s;\n" % table_name   746    747         # Write the corresponding definition.   748    749         print >>f_defs, """\   750 const __table %s = {   751     %s,   752     {   753         %s   754     }   755 };   756 """ % (table_name, structure_size,   757        ",\n        ".join(table))   758    759     def write_parameter_table(self, f_decls, f_defs, table_name, min_parameters,   760                               max_parameters, structure_size, table):   761    762         """   763         Write the declarations to 'f_decls' and definitions to 'f_defs' for   764         the object having the given 'table_name' and the given 'min_parameters',   765         'max_parameters' and 'structure_size', with 'table' details used to   766         populate the definition.   767         """   768    769         members = []   770         for t in table:   771             members.append("{.code=%s, .pos=%s}" % t)   772    773         print >>f_decls, "extern const __ptable %s;\n" % table_name   774    775         # Write the corresponding definition.   776    777         print >>f_defs, """\   778 const __ptable %s = {   779     .min=%s,   780     .max=%s,   781     .size=%s,   782     {   783         %s   784     }   785 };   786 """ % (table_name, min_parameters, max_parameters, structure_size,   787        ",\n        ".join(members))   788    789     def write_instance_structure(self, f_decls, path, structure_size, prefix="obj"):   790    791         """   792         Write a declaration to 'f_decls' for the object having the given 'path'   793         and the given 'structure_size'. The optional 'prefix' can be used to   794         modify the declared type.   795         """   796    797         # Write an instance-specific type definition for instances of classes.   798         # See: templates/types.h   799    800         print >>f_decls, """\   801 typedef struct {   802     const __table * table;   803     __pos pos;   804     __attr attrs[%s];   805 } %s;   806 """ % (structure_size, encode_symbol(prefix, path))   807    808     def write_structure(self, f_decls, f_defs, structure_name, table_name, structure, path=None):   809    810         """   811         Write the declarations to 'f_decls' and definitions to 'f_defs' for   812         the object having the given 'structure_name', the given 'table_name',   813         and the given 'structure' details used to populate the definition.   814         """   815    816         if f_decls:   817             print >>f_decls, "extern __obj %s;\n" % encode_path(structure_name)   818    819         structure_details = self.get_structure_definition(table_name, structure, path)   820         print >>f_defs, """\   821 __obj %s = %s;""" % (encode_path(structure_name), structure_details)   822    823     def get_structure_definition(self, table_name, structure, path=None):   824    825         """   826         Return a structure employing 'table_name', the given 'structure'   827         members, and any identifying static 'path'.   828         """   829    830         is_class = path and self.importer.get_object(path).has_kind("<class>")   831         pos = is_class and encode_pos(encode_type_attribute(path)) or str(self.instancepos)   832    833         return """\   834 {   835     &%s,   836     %s,   837     {   838         %s   839     }   840 }""" % (table_name, pos, ",\n        ".join(structure))   841    842     def get_argument_limits(self, path):   843    844         """   845         Return the argument minimum and maximum for the callable at 'path',   846         adding an argument position for a universal context.   847         """   848    849         parameters = self.importer.function_parameters[path]   850         defaults = self.importer.function_defaults.get(path)   851         num_parameters = len(parameters) + 1   852         return num_parameters - (defaults and len(defaults) or 0), num_parameters   853    854     def get_attribute_names(self, indexes):   855    856         """   857         Given a list of attribute table 'indexes', return a list of attribute   858         names.   859         """   860    861         all_attrnames = self.optimiser.all_attrnames   862         attrnames = []   863         for i in indexes:   864             if i is None:   865                 attrnames.append(None)   866             else:   867                 attrnames.append(all_attrnames[i])   868         return attrnames   869    870     def get_static_attributes(self, kind, name, attrnames):   871    872         """   873         Return a mapping of attribute names to paths for attributes belonging   874         to objects of the given 'kind' (being "<class>" or "<module>") with   875         the given 'name' and supporting the given 'attrnames'.   876         """   877    878         attrs = {}   879    880         for attrname in attrnames:   881             if attrname is None:   882                 continue   883             if kind == "<class>":   884                 path = self.importer.all_class_attrs[name][attrname]   885             elif kind == "<module>":   886                 path = "%s.%s" % (name, attrname)   887             else:   888                 continue   889    890             # The module may be hidden.   891    892             attr = self.importer.get_object(path)   893             if not attr:   894                 module = self.importer.hidden.get(path)   895                 if module:   896                     attr = Reference(module.name, "<module>")   897             attrs[attrname] = attr   898    899         return attrs   900    901     def get_instance_attributes_for_reference(self, ref):   902    903         """   904         Return a mapping of attribute names to references for attributes   905         belonging to instances of the given 'ref' referencing a class.   906         """   907    908         cls = ref.get_origin()   909         indexes = self.optimiser.attr_table[ref]   910         attrnames = self.get_attribute_names(indexes)   911         return self.get_instance_attributes(cls, attrnames)   912    913     def get_instance_attributes(self, name, attrnames):   914    915         """   916         Return a mapping of attribute names to references for attributes   917         belonging to instances of the class with the given 'name', where the   918         given 'attrnames' are supported.   919         """   920    921         consts = self.importer.all_instance_attr_constants[name]   922         attrs = {}   923         for attrname in attrnames:   924             if attrname is None:   925                 continue   926             const = consts.get(attrname)   927             attrs[attrname] = const or Reference("<var>", "%s.%s" % (name, attrname))   928         return attrs   929    930     def populate_table(self, path, table):   931    932         """   933         Traverse the attributes in the determined order for the structure having   934         the given 'path', adding entries to the attribute 'table'.   935         """   936    937         for attrname in self.optimiser.structures[path]:   938    939             # Handle gaps in the structure.   940    941             if attrname is None:   942                 table.append("0")   943             else:   944                 table.append(encode_code(attrname))   945    946     def populate_parameter_table(self, parameters, table):   947    948         """   949         Traverse the 'parameters' in the determined order, adding entries to the   950         attribute 'table'.   951         """   952    953         for value in parameters:   954    955             # Handle gaps in the structure.   956    957             if value is None:   958                 table.append(("0", "0"))   959             else:   960                 name, pos = value   961                 table.append((encode_symbol("pcode", name), pos))   962    963     def populate_function(self, path, function_instance_attrs):   964    965         """   966         Populate a structure for the function with the given 'path'. The given   967         'attrs' provide the instance attributes.   968         """   969    970         structure = []   971         self.populate_structure(Reference("<function>", path), function_instance_attrs, "<instance>", structure)   972    973         # Append default members.   974    975         self.append_defaults(path, structure)   976         return structure   977    978     def populate_structure(self, ref, attrs, kind, structure):   979    980         """   981         Traverse the attributes in the determined order for the structure having   982         the given 'ref' whose members are provided by the 'attrs' mapping, in a   983         structure of the given 'kind', adding entries to the object 'structure'.   984         """   985    986         # Populate function instance structures for functions.   987    988         if ref.has_kind("<function>"):   989             origin = self.function_type   990             structure_ref = Reference("<instance>", self.function_type)   991    992         # Otherwise, just populate the appropriate structures.   993    994         else:   995             origin = ref.get_origin()   996             structure_ref = ref   997    998         for attrname in self.optimiser.structures[structure_ref]:   999   1000             # Handle gaps in the structure.  1001   1002             if attrname is None:  1003                 structure.append("__NULL")  1004   1005             # Handle non-constant and constant members.  1006   1007             else:  1008                 attr = attrs[attrname]  1009   1010                 # Special function pointer member.  1011   1012                 if attrname == "__fn__":  1013   1014                     # Classes offer instantiators which can be called without a  1015                     # context.  1016   1017                     if kind == "<class>":  1018                         attr = encode_instantiator_pointer(attr)  1019                     else:  1020                         attr = encode_function_pointer(attr)  1021   1022                     structure.append("{.fn=%s}" % attr)  1023                     continue  1024   1025                 # Special argument specification member.  1026   1027                 elif attrname == "__args__":  1028                     signature = self.get_signature_for_callable(ref.get_origin())  1029                     ptable = encode_tablename("<function>", signature)  1030   1031                     structure.append("{.ptable=&%s}" % ptable)  1032                     continue  1033   1034                 # Special internal data member.  1035   1036                 elif attrname == "__data__":  1037                     structure.append("{.%s=%s}" % (  1038                                      encode_literal_constant_member(attr),  1039                                      encode_literal_constant_value(attr)))  1040                     continue  1041   1042                 # Special internal size member.  1043   1044                 elif attrname == "__size__":  1045                     structure.append("{.intvalue=%d}" % attr)  1046                     continue  1047   1048                 # Special internal key member.  1049   1050                 elif attrname == "__key__":  1051                     structure.append("{.code=%s, .pos=%s}" % (attr and encode_code(attr) or "0",  1052                                                               attr and encode_pos(attr) or "0"))  1053                     continue  1054   1055                 # Special cases.  1056   1057                 elif attrname in ("__file__", "__name__"):  1058                     path = ref.get_origin()  1059                     value_type = self.string_type  1060   1061                     # Provide constant values. These must match the values  1062                     # originally recorded during inspection.  1063   1064                     if attrname == "__file__":  1065                         module = self.importer.get_module(path)  1066                         value = module.filename  1067   1068                     # Function and class names are leafnames.  1069   1070                     elif attrname == "__name__" and not ref.has_kind("<module>"):  1071                         value = path.rsplit(".", 1)[-1]  1072   1073                     # All other names just use the object path information.  1074   1075                     else:  1076                         value = path  1077   1078                     encoding = None  1079   1080                     local_number = self.importer.all_constants[path][(value, value_type, encoding)]  1081                     constant_name = "$c%d" % local_number  1082                     attr_path = "%s.%s" % (path, constant_name)  1083                     constant_number = self.optimiser.constant_numbers[attr_path]  1084                     constant_value = "__const%s" % constant_number  1085                     structure.append("%s /* %s */" % (constant_value, attrname))  1086                     continue  1087   1088                 elif attrname == "__parent__":  1089                     path = ref.get_origin()  1090   1091                     # Parents of classes and functions are derived from their  1092                     # object paths.  1093   1094                     value = path.rsplit(".", 1)[0]  1095                     structure.append("{.value=&%s}" % encode_path(value))  1096                     continue  1097   1098                 # Special context member.  1099                 # Set the context depending on the kind of attribute.  1100                 # For methods:          <parent>  1101                 # For other attributes: __NULL  1102   1103                 elif attrname == "__context__":  1104                     path = ref.get_origin()  1105   1106                     # Contexts of methods are derived from their object paths.  1107   1108                     context = "0"  1109   1110                     if ref.get_kind() == "<function>":  1111                         parent = path.rsplit(".", 1)[0]  1112                         if self.importer.classes.has_key(parent):  1113                             context = "&%s" % encode_path(parent)  1114   1115                     structure.append("{.value=%s}" % context)  1116                     continue  1117   1118                 # Special class relationship attributes.  1119   1120                 elif is_type_attribute(attrname):  1121                     structure.append("{.value=&%s}" % encode_path(decode_type_attribute(attrname)))  1122                     continue  1123   1124                 # All other kinds of members.  1125   1126                 structure.append(self.encode_member(origin, attrname, attr, kind))  1127   1128     def encode_member(self, path, name, ref, structure_type):  1129   1130         """  1131         Encode within the structure provided by 'path', the member whose 'name'  1132         provides 'ref', within the given 'structure_type'.  1133         """  1134   1135         kind = ref.get_kind()  1136         origin = ref.get_origin()  1137   1138         # References to constant literals.  1139   1140         if kind == "<instance>" and ref.is_constant_alias():  1141             alias = ref.get_name()  1142   1143             # Use the alias directly if appropriate.  1144   1145             if alias.startswith("$c"):  1146                 constant_value = encode_literal_constant(alias[2:])  1147                 return "%s /* %s */" % (constant_value, name)  1148   1149             # Obtain a constant value directly assigned to the attribute.  1150   1151             if self.optimiser.constant_numbers.has_key(alias):  1152                 constant_number = self.optimiser.constant_numbers[alias]  1153                 constant_value = encode_literal_constant(constant_number)  1154                 return "%s /* %s */" % (constant_value, name)  1155   1156         # Usage of predefined constants, currently only None supported.  1157   1158         if kind == "<instance>" and origin == self.none_type:  1159             attr_path = encode_predefined_reference(self.none_value)  1160             return "{.value=&%s} /* %s */" % (attr_path, name)  1161   1162         # Predefined constant members.  1163   1164         if (path, name) in self.predefined_constant_members:  1165             attr_path = encode_predefined_reference("%s.%s" % (path, name))  1166             return "{.value=&%s} /* %s */" % (attr_path, name)  1167   1168         # General undetermined members.  1169   1170         if kind in ("<var>", "<instance>"):  1171             attr_path = encode_predefined_reference(self.none_value)  1172             return "{.value=&%s} /* %s */" % (attr_path, name)  1173   1174         else:  1175             return "{.value=&%s}" % encode_path(origin)  1176   1177     def append_defaults(self, path, structure):  1178   1179         """  1180         For the given 'path', append default parameter members to the given  1181         'structure'.  1182         """  1183   1184         for name, default in self.importer.function_defaults.get(path):  1185             structure.append(self.encode_member(path, name, default, "<instance>"))  1186   1187     def write_instantiator(self, f_code, f_signatures, path, init_ref):  1188   1189         """  1190         Write an instantiator to 'f_code', with a signature to 'f_signatures',  1191         for instances of the class with the given 'path', with 'init_ref' as the  1192         initialiser function reference.  1193   1194         NOTE: This also needs to initialise any __fn__ and __args__ members  1195         NOTE: where __call__ is provided by the class.  1196         """  1197   1198         parameters = self.importer.function_parameters[init_ref.get_origin()]  1199   1200         print >>f_code, """\  1201 __attr %s(__attr __args[])  1202 {  1203     /* Allocate the structure. */  1204     __args[0] = __NEWINSTANCE(%s);  1205   1206     /* Call the initialiser. */  1207     %s(__args);  1208   1209     /* Return the allocated object details. */  1210     return __args[0];  1211 }  1212 """ % (  1213     encode_instantiator_pointer(path),  1214     encode_path(path),  1215     encode_function_pointer(init_ref.get_origin())  1216     )  1217   1218         print >>f_signatures, "#define __HAVE_%s" % encode_path(path)  1219         print >>f_signatures, "__attr %s(__attr[]);" % encode_instantiator_pointer(path)  1220   1221         # Write additional literal instantiators. These do not call the  1222         # initialisers but instead populate the structures directly.  1223   1224         if path in self.literal_instantiator_types:  1225             if path in self.literal_mapping_types:  1226                 style = "mapping"  1227             else:  1228                 style = "sequence"  1229   1230             print >>f_code, """\  1231 __attr %s(__attr __args[], __pos number)  1232 {  1233     /* Allocate the structure. */  1234     __args[0] = __NEWINSTANCE(%s);  1235   1236     /* Allocate a structure for the data and set it on the __data__ attribute. */  1237     %s(__args, number);  1238   1239     /* Return the allocated object details. */  1240     return __args[0];  1241 }  1242 """ % (  1243     encode_literal_instantiator(path),  1244     encode_path(path),  1245     encode_literal_data_initialiser(style)  1246     )  1247   1248             print >>f_signatures, "__attr %s(__attr[], __pos);" % encode_literal_instantiator(path)  1249   1250     def write_main_program(self, f_code, f_signatures):  1251   1252         """  1253         Write the main program to 'f_code', invoking the program's modules. Also  1254         write declarations for module main functions to 'f_signatures'.  1255         """  1256   1257         print >>f_code, """\  1258 int main(int argc, char *argv[])  1259 {  1260     __exc __tmp_exc;  1261   1262     GC_INIT();  1263   1264     __Try  1265     {"""  1266   1267         for name in self.importer.order_modules():  1268             function_name = "__main_%s" % encode_path(name)  1269             print >>f_signatures, "void %s();" % function_name  1270   1271             # Omit the native modules.  1272   1273             parts = name.split(".")  1274   1275             if parts[0] != "native":  1276                 print >>f_code, """\  1277         %s();""" % function_name  1278   1279         print >>f_code, """\  1280     }  1281     __Catch(__tmp_exc)  1282     {  1283         if (__ISINSTANCE(__tmp_exc.arg, __ATTRVALUE(&__builtins___exception_system_SystemExit)))  1284             return __load_via_object(  1285                 __load_via_object(__tmp_exc.arg.value, __data__).value,  1286                 value).intvalue;  1287   1288         fprintf(stderr, "Program terminated due to exception: %%s.\\n",  1289                 __load_via_object(  1290                     %s(__ARGS(__NULL, __tmp_exc.arg)).value,  1291                     __data__).strvalue);  1292         return 1;  1293     }  1294   1295     return 0;  1296 }  1297 """ % encode_function_pointer("__builtins__.str.str")  1298   1299 # vim: tabstop=4 expandtab shiftwidth=4