Lichen

generator.py

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