Lichen

generator.py

938:799711337453
5 months ago Paul Boddie Renamed the string class to str, replacing the str function with the new_str function, this being invoked specially by the string instantiation function. As with the Unicode type renaming, a more general instantiation mechanism might permit the new_str function to be part of the functionality of the str or basestring classes.
     1 #!/usr/bin/env python     2      3 """     4 Generate C code from object layouts and other deduced information.     5      6 Copyright (C) 2015-2019, 2021 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.str"    50     tuple_type = "__builtins__.tuple.tuple"    51     type_type = "__builtins__.core.type"    52     unicode_type = "__builtins__.unicode.unicode"    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)   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    293                 # Set a special callable attribute on the instance.   294    295                 function_instance_attrs["__fn__"] = path   296                 function_instance_attrs["__args__"] = path   297    298                 structure = self.populate_function(path, function_instance_attrs)   299                 self.write_structure(f_decls, f_defs, path, table_name, structure)   300    301                 # Functions with defaults need to declare instance structures.   302    303                 if self.importer.function_defaults.get(path):   304                     self.write_instance_structure(f_decls, path)   305                     extra_function_instances.append(path)   306    307                 # Write function declarations.   308                 # Signature: __attr <name>(...);   309    310                 parameters = self.importer.function_parameters[path]   311                 l = ["__attr"] * (len(parameters) + 1)   312                 print >>f_signatures, "__attr %s(%s);" % (encode_function_pointer(path), ", ".join(l))   313    314             # Generate parameter table size data.   315    316             min_parameters = {}   317             max_parameters = {}   318             size_parameters = {}   319             all_max_parameters = set()   320    321             # Consolidate parameter tables for instantiators and functions.   322    323             parameter_tables = set()   324    325             for path, function_path in self.callables.items():   326                 argmin, argmax = self.get_argument_limits(function_path)   327    328                 # Obtain the parameter table members.   329    330                 parameters = self.optimiser.parameters[function_path]   331                 if not parameters:   332                     parameters = ()   333                 else:   334                     parameters = tuple(parameters)   335    336                 # Define each table in terms of the members and the minimum   337                 # number of arguments.   338    339                 parameter_tables.add((argmin, parameters))   340                 signature = self.get_parameter_signature(argmin, parameters)   341    342                 # Record the minimum number of arguments, the maximum number,   343                 # and the size of the table.   344    345                 min_parameters[signature] = argmin   346                 max_parameters[signature] = argmax   347                 size_parameters[signature] = len(parameters)   348                 all_max_parameters.add(argmax)   349    350             self.write_size_constants(f_consts, "pmin", min_parameters, 0)   351             self.write_size_constants(f_consts, "pmax", max_parameters, 0)   352             self.write_size_constants(f_consts, "psize", size_parameters, 0)   353    354             # Generate parameter tables for distinct function signatures.   355    356             for argmin, parameters in parameter_tables:   357                 self.make_parameter_table(f_decls, f_defs, argmin, parameters)   358    359             # Generate predefined constants.   360    361             for path, name in self.predefined_constant_members:   362                 self.make_predefined_constant(f_decls, f_defs, path, name)   363    364             # Generate literal constants.   365    366             for constant, n in self.optimiser.constants.items():   367                 self.make_literal_constant(f_decls, f_defs, n, constant)   368    369             # Generate a common integer instance object, referenced when integer   370             # attributes are accessed.   371    372             self.make_common_integer(f_decls, f_defs)   373    374             # Finish the main source file.   375    376             self.write_main_program(f_code, f_signatures)   377    378             # Record size information for certain function instances as well as   379             # for classes, modules and other instances.   380    381             size_tables = {}   382    383             for kind in ["<class>", "<module>", "<instance>"]:   384                 size_tables[kind] = {}   385    386             # Generate structure size data.   387    388             for ref, structure in self.optimiser.structures.items():   389                 size_tables[ref.get_kind()][ref.get_origin()] = len(structure)   390    391             for path in extra_function_instances:   392                 defaults = self.importer.function_defaults[path]   393                 size_tables["<instance>"][path] = size_tables["<instance>"][self.function_type] + len(defaults)   394    395             size_tables = size_tables.items()   396             size_tables.sort()   397    398             for kind, sizes in size_tables:   399                 self.write_size_constants(f_consts, kind, sizes, 0)   400    401             # Generate parameter codes.   402    403             self.write_code_constants(f_consts, self.optimiser.all_paramnames,   404                                       self.optimiser.arg_locations,   405                                       "pcode", "ppos", encode_pcode, encode_ppos)   406    407             # Generate attribute codes.   408    409             self.write_code_constants(f_consts, self.optimiser.all_attrnames,   410                                       self.optimiser.locations,   411                                       "code", "pos", encode_code, encode_pos)   412    413             # Generate macros for calls.   414    415             all_max_parameters = list(all_max_parameters)   416             all_max_parameters.sort()   417    418             for argmax in all_max_parameters:   419                 l = []   420                 argnum = 0   421                 while argnum < argmax:   422                     l.append("ARGS[%d]" % argnum)   423                     argnum += 1   424    425                 print >>f_call_macros, "#define __CALL%d(FN, ARGS) (FN(%s))" % (argmax, ", ".join(l))   426    427             # Generate a generic invocation function.   428    429             print >>f_call_macros, "__attr __call_with_args(__attr (*fn)(), __attr args[], unsigned int n);"   430    431             print >>f_calls, """\   432 #include "types.h"   433 #include "calls.h"   434    435 __attr __call_with_args(__attr (*fn)(), __attr args[], unsigned int n)   436 {   437     switch (n)   438     {"""   439    440             for argmax in all_max_parameters:   441                 print >>f_calls, """\   442         case %d: return __CALL%d(fn, args);""" % (argmax, argmax)   443    444             print >>f_calls, """\   445         default: return __NULL;   446     }   447 }"""   448    449             # Output more boilerplate.   450    451             print >>f_consts, """\   452    453 #endif /* __PROGCONSTS_H__ */"""   454    455             print >>f_decls, """\   456    457 #define __FUNCTION_TYPE %s   458 #define __FUNCTION_INSTANCE_SIZE %s   459 #define __TYPE_CLASS_TYPE %s   460 #define __TYPE_CLASS_POS %s   461 #define __TYPE_CLASS_CODE %s   462    463 #endif /* __PROGTYPES_H__ */""" % (   464     encode_path(self.function_type),   465     encode_size("<instance>", self.function_type),   466     encode_path(self.type_type),   467     encode_pos(encode_type_attribute(self.type_type)),   468     encode_code(encode_type_attribute(self.type_type)),   469     )   470    471             print >>f_signatures, """\   472    473 #endif /* __MAIN_H__ */"""   474    475             print >>f_call_macros, """\   476    477 #endif /* __CALLS_H__ */"""   478    479         finally:   480             f_consts.close()   481             f_defs.close()   482             f_decls.close()   483             f_signatures.close()   484             f_code.close()   485             f_calls.close()   486             f_call_macros.close()   487    488     def write_scripts(self, debug, gc_sections):   489    490         "Write scripts used to build the program."   491    492         # Options affect compiling and linking.   493    494         f_options = open(join(self.output, "options.mk"), "w")   495         try:   496             if debug:   497                 print >>f_options, "CFLAGS = -g"   498             else:   499                 print >>f_options, "CFLAGS = -O2"   500    501             if gc_sections:   502                 print >>f_options, "include gc_sections.mk"   503    504         finally:   505             f_options.close()   506    507         # Native and module definitions divide the program modules into native   508         # and generated code.   509    510         f_native = open(join(self.output, "native.mk"), "w")   511         f_modules = open(join(self.output, "modules.mk"), "w")   512         try:   513             # Identify modules used by the program.   514    515             native_modules = [join("native", "common.c")]   516             modules = []   517    518             for name in self.importer.modules.keys():   519                 parts = name.split(".", 1)   520    521                 # Identify source files to be built.   522    523                 if parts[0] == "native":   524                     native_modules.append(join("native", "%s.c" % parts[1]))   525                 else:   526                     modules.append(join("src", "%s.c" % name))   527    528             print >>f_native, "SRC =", " ".join(native_modules)   529             print >>f_modules, "SRC +=", " ".join(modules)   530    531         finally:   532             f_native.close()   533             f_modules.close()   534    535         # Instance position configuration uses the position of the ubiquitous   536         # __class__ attribute as a means of indicating that an object is an   537         # instance. Classes employ special identifying attributes that are   538         # positioned elsewhere and thus cannot be in the same location as the   539         # __class__ attribute.   540    541         f_instancepos = open(join(self.output, "instancepos.h"), "w")   542         try:   543             print >>f_instancepos, """\   544 #ifndef __INSTANCEPOS   545 #define __INSTANCEPOS %d   546 #endif   547 """ % self.instancepos   548         finally:   549             f_instancepos.close()   550    551     def make_literal_constant(self, f_decls, f_defs, n, constant):   552    553         """   554         Write literal constant details to 'f_decls' (to declare a structure) and   555         to 'f_defs' (to define the contents) for the constant with the number   556         'n' with the given 'constant'.   557         """   558    559         value, value_type, encoding = constant   560    561         # Do not generate individual integer constants.   562    563         if value_type == self.int_type:   564             return   565    566         const_path = encode_literal_constant(n)   567         structure_name = encode_literal_reference(n)   568    569         ref = Reference("<instance>", value_type)   570         self.make_constant(f_decls, f_defs, ref, const_path, structure_name, value, encoding)   571    572     def make_predefined_constant(self, f_decls, f_defs, path, name):   573    574         """   575         Write predefined constant details to 'f_decls' (to declare a structure)   576         and to 'f_defs' (to define the contents) for the constant located in   577         'path' with the given 'name'.   578         """   579    580         # Determine the details of the constant.   581    582         attr_path = "%s.%s" % (path, name)   583         structure_name = encode_predefined_reference(attr_path)   584         ref = self.importer.get_object(attr_path)   585    586         self.make_constant(f_decls, f_defs, ref, attr_path, structure_name)   587    588     def make_common_integer(self, f_decls, f_defs):   589    590         """   591         Write common integer instance details to 'f_decls' (to declare a   592         structure) and to 'f_defs' (to define the contents).   593         """   594    595         ref = Reference("<instance>", self.int_type)   596         self.make_constant(f_decls, f_defs, ref, "__common_integer", "__common_integer_obj")   597    598     def make_constant(self, f_decls, f_defs, ref, const_path, structure_name, data=None, encoding=None):   599    600         """   601         Write constant details to 'f_decls' (to declare a structure) and to   602         'f_defs' (to define the contents) for the constant described by 'ref'   603         having the given 'const_path' (providing an attribute for the constant)   604         and 'structure_name' (for the constant structure itself).   605    606         The additional 'data' and 'encoding' are used to describe specific   607         values.   608         """   609    610         # Obtain the attributes.   611    612         cls = ref.get_origin()   613         attrnames = self.optimiser.all_attrs[ref]   614         attrs = self.get_instance_attributes(cls, attrnames)   615    616         # Set the data, if provided.   617    618         if data is not None:   619             attrs["__data__"] = data   620    621             # Also set a key for dynamic attribute lookup, if a string.   622    623             if attrs.has_key("__key__"):   624                 if data in self.optimiser.all_attrnames:   625                     attrs["__key__"] = data   626                 else:   627                     attrs["__key__"] = None   628    629             # Initialise the size, if a string.   630    631             if attrs.has_key("__size__"):   632                 attrs["__size__"] = len(data)   633    634         # Define Unicode constant encoding details.   635    636         if cls == self.unicode_type:   637    638             # Reference the encoding's own constant value.   639    640             if encoding:   641                 n = self.optimiser.constants[(encoding, self.string_type, None)]   642    643                 # Employ a special alias that will be tested specifically in   644                 # encode_member.   645    646                 encoding_ref = Reference("<instance>", self.string_type, "$c%s" % n)   647    648             # Use None where no encoding was indicated.   649    650             else:   651                 encoding_ref = Reference("<instance>", self.none_type)   652    653             attrs["encoding"] = encoding_ref   654    655         # Define the structure details. An object is created for the constant,   656         # but an attribute is provided, referring to the object, for access to   657         # the constant in the program.   658    659         structure = []   660         table_name = encode_tablename("<instance>", cls)   661         self.populate_structure(ref, attrs, ref.get_kind(), structure)   662         self.write_structure(f_decls, f_defs, structure_name, table_name, structure)   663    664         # Define a macro for the constant.   665    666         attr_name = encode_path(const_path)   667         print >>f_decls, "#define %s __ATTRVALUE(&%s)" % (attr_name, structure_name)   668    669     def make_parameter_table(self, f_decls, f_defs, argmin, parameters):   670    671         """   672         Write parameter table details to 'f_decls' (to declare a table) and to   673         'f_defs' (to define the contents) for the given 'argmin' and   674         'parameters'.   675         """   676    677         # Use a signature for the table name instead of a separate name for each   678         # function.   679    680         signature = self.get_parameter_signature(argmin, parameters)   681         table_name = encode_tablename("<function>", signature)   682         min_parameters = encode_size("pmin", signature)   683         max_parameters = encode_size("pmax", signature)   684         structure_size = encode_size("psize", signature)   685    686         table = []   687         self.populate_parameter_table(parameters, table)   688         self.write_parameter_table(f_decls, f_defs, table_name, min_parameters, max_parameters, structure_size, table)   689    690     def get_parameter_signature(self, argmin, parameters):   691    692         "Return a signature for the given 'argmin' and 'parameters'."   693    694         l = [str(argmin)]   695         for parameter in parameters:   696             if parameter is None:   697                 l.append("")   698             else:   699                 name, pos = parameter   700                 l.append("%s_%s" % (name, pos))   701         return l and "__".join(l) or "__void"   702    703     def get_signature_for_callable(self, path):   704    705         "Return the signature for the callable with the given 'path'."   706    707         function_path = self.callables[path]   708         argmin, argmax = self.get_argument_limits(function_path)   709         parameters = self.optimiser.parameters[function_path]   710         return self.get_parameter_signature(argmin, parameters)   711    712     def write_size_constants(self, f_consts, size_prefix, sizes, padding):   713    714         """   715         Write size constants to 'f_consts' for the given 'size_prefix', using   716         the 'sizes' dictionary to populate the definition, adding the given   717         'padding' to the basic sizes.   718         """   719    720         print >>f_consts, "enum %s {" % encode_size(size_prefix)   721         first = True   722         for path, size in sizes.items():   723             if not first:   724                 print >>f_consts, ","   725             else:   726                 first = False   727             f_consts.write("    %s = %d" % (encode_size(size_prefix, path), size + padding))   728         print >>f_consts, "\n    };"   729    730     def write_code_constants(self, f_consts, attrnames, locations, code_prefix,   731                              pos_prefix, code_encoder, pos_encoder):   732    733         """   734         Write code constants to 'f_consts' for the given 'attrnames' and   735         attribute 'locations'.   736         """   737    738         print >>f_consts, "enum %s {" % encode_symbol(code_prefix)   739         first = True   740         for i, attrname in enumerate(attrnames):   741             if not first:   742                 print >>f_consts, ","   743             else:   744                 first = False   745             f_consts.write("    %s = %d" % (code_encoder(attrname), i))   746         print >>f_consts, "\n    };"   747    748         print >>f_consts, "enum %s {" % encode_symbol(pos_prefix)   749         first = True   750         for i, attrnames in enumerate(locations):   751             for attrname in attrnames:   752                 if not first:   753                     print >>f_consts, ","   754                 else:   755                     first = False   756                 f_consts.write("    %s = %d" % (pos_encoder(attrname), i))   757         print >>f_consts, "\n    };"   758    759     def write_table(self, f_decls, f_defs, table_name, structure_size, table):   760    761         """   762         Write the declarations to 'f_decls' and definitions to 'f_defs' for   763         the object having the given 'table_name' and the given 'structure_size',   764         with 'table' details used to populate the definition.   765         """   766    767         print >>f_decls, "extern const __table %s;\n" % table_name   768    769         # Write the corresponding definition.   770    771         print >>f_defs, """\   772 const __table %s = {   773     %s,   774     {   775         %s   776     }   777 };   778 """ % (table_name, structure_size,   779        ",\n        ".join(table))   780    781     def write_parameter_table(self, f_decls, f_defs, table_name, min_parameters,   782                               max_parameters, structure_size, table):   783    784         """   785         Write the declarations to 'f_decls' and definitions to 'f_defs' for   786         the object having the given 'table_name' and the given 'min_parameters',   787         'max_parameters' and 'structure_size', with 'table' details used to   788         populate the definition.   789         """   790    791         members = []   792         for t in table:   793             members.append("{.code=%s, .pos=%s}" % t)   794    795         print >>f_decls, "extern const __ptable %s;\n" % table_name   796    797         # Write the corresponding definition.   798    799         print >>f_defs, """\   800 const __ptable %s = {   801     .min=%s,   802     .max=%s,   803     .size=%s,   804     {   805         %s   806     }   807 };   808 """ % (table_name, min_parameters, max_parameters, structure_size,   809        ",\n        ".join(members))   810    811     def write_instance_structure(self, f_decls, path):   812    813         """   814         Write a declaration to 'f_decls' for the object having the given 'path'.   815         """   816    817         structure_size = encode_size("<instance>", path)   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         # Special-case the integer type.  1204   1205         # Here, the __builtins__.int.new_int function is called with the  1206         # initialiser's parameters.  1207   1208         if path == self.int_type:  1209             print >>f_code, """\  1210 __attr %s(__attr __self, __attr number_or_string, __attr base)  1211 {  1212     return __fn___builtins___int_new_int(__NULL, number_or_string, base);  1213 }  1214 """ % (  1215                 encode_instantiator_pointer(path),  1216                 )  1217   1218         # Special-case the string types.  1219   1220         # Here, the __builtins__.str.new_str function is called with the  1221         # initialiser's parameter.  1222   1223         elif path == self.string_type:  1224             print >>f_code, """\  1225 __attr %s(__attr __self, __attr obj)  1226 {  1227     return __fn___builtins___str_new_str(__NULL, obj);  1228 }  1229 """ % (  1230                 encode_instantiator_pointer(path),  1231                 )  1232   1233         # Generic instantiation support.  1234   1235         else:  1236             print >>f_code, """\  1237 __attr %s(__attr __self%s)  1238 {  1239     return %s(__NEWINSTANCE(%s)%s);  1240 }  1241 """ % (  1242                 encode_instantiator_pointer(path),  1243                 l and ", %s" % ",".join(l) or "",  1244                 encode_function_pointer(initialiser),  1245                 encode_path(path),  1246                 parameters and ", %s" % ", ".join(parameters) or ""  1247                 )  1248   1249         # Signature: __new_typename(__attr __self, ...)  1250   1251         print >>f_signatures, "__attr %s(__attr __self%s);" % (  1252             encode_instantiator_pointer(path),  1253             l and ", %s" % ", ".join(l) or ""  1254             )  1255   1256         print >>f_signatures, "#define __HAVE_%s" % encode_path(path)  1257   1258     def write_main_program(self, f_code, f_signatures):  1259   1260         """  1261         Write the main program to 'f_code', invoking the program's modules. Also  1262         write declarations for module main functions to 'f_signatures'.  1263         """  1264   1265         print >>f_code, """\  1266 int main(int argc, char *argv[])  1267 {  1268     __exc __tmp_exc;  1269   1270     GC_INIT();  1271   1272     __Try  1273     {"""  1274   1275         for name in self.importer.order_modules():  1276             function_name = "__main_%s" % encode_path(name)  1277             print >>f_signatures, "void %s();" % function_name  1278   1279             # Omit the native modules.  1280   1281             parts = name.split(".")  1282   1283             if parts[0] != "native":  1284                 print >>f_code, """\  1285         %s();""" % function_name  1286   1287         print >>f_code, """\  1288     }  1289     __Catch(__tmp_exc)  1290     {  1291         if (__ISINSTANCE(__tmp_exc.arg, __ATTRVALUE(&__builtins___exception_system_SystemExit)))  1292             return __TOINT(__load_via_object(__VALUE(__tmp_exc.arg), value));  1293   1294         fprintf(stderr, "Program terminated due to exception: %%s.\\n",  1295                 __load_via_object(  1296                     __VALUE(%s(__NULL, __tmp_exc.arg)),  1297                     __data__).strvalue);  1298         return 1;  1299     }  1300   1301     return 0;  1302 }  1303 """ % encode_instantiator_pointer("__builtins__.str.str")  1304   1305 # vim: tabstop=4 expandtab shiftwidth=4