Lichen

generator.py

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