Lichen

generator.py

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