Lichen

generator.py

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