Lichen

generator.py

935:7dec14799571
6 months ago 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-2019, 2021 Paul Boddie <paul@boddie.org.uk>     7      8 This program is free software; you can redistribute it and/or modify it under     9 the terms of the GNU General Public License as published by the Free Software    10 Foundation; either version 3 of the License, or (at your option) any later    11 version.    12     13 This program is distributed in the hope that it will be useful, but WITHOUT    14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    15 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    16 details.    17     18 You should have received a copy of the GNU General Public License along with    19 this program.  If not, see <http://www.gnu.org/licenses/>.    20 """    21     22 from common import CommonOutput, copy    23 from encoders import encode_code, \    24                      encode_function_pointer, \    25                      encode_instantiator_pointer, \    26                      encode_literal_constant, encode_literal_constant_member, \    27                      encode_literal_constant_size, encode_literal_constant_value, \    28                      encode_literal_reference, \    29                      encode_path, encode_pcode, encode_pos, encode_ppos, \    30                      encode_predefined_reference, encode_size, \    31                      encode_symbol, encode_tablename, \    32                      encode_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.unicode"    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 "signals.h"   196 #include "types.h"   197 #include "exceptions.h"   198 #include "ops.h"   199 #include "progconsts.h"   200 #include "progtypes.h"   201 #include "main.h"   202 #include "progops.h"   203 #include "calls.h"   204 """   205    206             print >>f_call_macros, """\   207 #ifndef __CALLS_H__   208 #define __CALLS_H__   209    210 #include "types.h"   211 """   212    213             # Generate table and structure data.   214    215             function_instance_attrs = None   216             objects = self.optimiser.all_attrs.items()   217             objects.sort()   218    219             self.callables = {}   220    221             for ref, attrnames in objects:   222                 kind = ref.get_kind()   223                 path = ref.get_origin()   224                 table_name = encode_tablename(kind, path)   225                 structure_size = encode_size(kind, path)   226    227                 # Generate structures for classes and modules.   228    229                 if kind != "<instance>":   230                     structure = []   231                     trailing = []   232                     attrs = self.get_static_attributes(kind, path, attrnames)   233    234                     # Set a special instantiator on the class.   235    236                     if kind == "<class>":   237    238                         # Write instantiator declarations based on the   239                         # applicable initialiser.   240    241                         init_ref = attrs["__init__"]   242    243                         # Write instantiator definitions.   244    245                         self.write_instantiator(f_code, f_signatures, path, init_ref)   246    247                         # Record the callable for parameter table generation.   248    249                         self.callables[path] = init_ref.get_origin()   250    251                         # Define special attributes.   252    253                         attrs["__fn__"] = path   254                         attrs["__args__"] = path   255    256                     self.populate_structure(Reference(kind, path), attrs, kind, structure)   257                     self.populate_trailing(Reference(kind, path), attrs, trailing)   258    259                     if kind == "<class>":   260                         self.write_instance_structure(f_decls, path)   261    262                     self.write_structure(f_decls, f_defs, path, table_name,   263                                          structure, trailing, ref)   264    265                 # Record function instance details for function generation below.   266    267                 else:   268                     attrs = self.get_instance_attributes(path, attrnames)   269                     if path == self.function_type:   270                         function_instance_attrs = attrs   271    272                         # Record the callable for parameter table generation.   273    274                         self.callables[path] = path   275    276                 # Write a table for all objects.   277    278                 table = []   279                 self.populate_table(Reference(kind, path), table)   280                 self.write_table(f_decls, f_defs, table_name, structure_size, table)   281    282             # Generate function instances.   283    284             functions = self.importer.function_parameters.keys()   285             functions.sort()   286             extra_function_instances = []   287    288             for path in functions:   289    290                 # Instantiators are generated above.   291    292                 if self.importer.classes.has_key(path) or not self.importer.get_object(path):   293                     continue   294    295                 # Record the callable for parameter table generation.   296    297                 self.callables[path] = path   298    299                 # Define the structure details.   300    301                 cls = self.function_type   302                 table_name = encode_tablename("<instance>", cls)   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)   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):   841    842         """   843         Write a declaration to 'f_decls' for the object having the given 'path'.   844         """   845    846         structure_size = encode_size("<instance>", path)   847    848         # Write an instance-specific type definition for instances of classes.   849         # See: templates/types.h   850    851         trailing_area = path in self.trailing_data_types and encode_trailing_area(path) or ""   852    853         print >>f_decls, """\   854 typedef struct {   855     const __table * table;   856     __pos pos;   857     __attr attrs[%s];   858 %s   859 } %s;   860 """ % (structure_size, trailing_area, encode_symbol("obj", path))   861    862     def write_structure(self, f_decls, f_defs, structure_name, table_name,   863                         structure, trailing, ref):   864    865         """   866         Write the declarations to 'f_decls' and definitions to 'f_defs' for   867         the object having the given 'structure_name', the given 'table_name',   868         the given 'structure' details and any 'trailing' member details, used to   869         populate the definition.   870         """   871    872         origin = ref.get_origin()   873         pos = ref.has_kind("<class>") and encode_pos(encode_type_attribute(origin)) or str(self.instancepos)   874    875         obj_type = ref.has_kind("<instance>") and encode_symbol("obj", origin) or "__obj"   876         obj_name = encode_path(structure_name)   877    878         if f_decls:   879             print >>f_decls, "extern %s %s;\n" % (obj_type, obj_name)   880    881         print >>f_defs, """\   882 %s %s = {   883     &%s,   884     %s,   885     {   886         %s   887     },   888     %s   889     };   890 """ % (   891             obj_type, obj_name,   892             table_name,   893             pos,   894             ",\n        ".join(structure),   895             trailing and ",\n    ".join(trailing) or "")   896    897     def get_argument_limits(self, path):   898    899         """   900         Return the argument minimum and maximum for the callable at 'path',   901         adding an argument position for a universal context.   902         """   903    904         parameters = self.importer.function_parameters[path]   905         defaults = self.importer.function_defaults.get(path)   906         num_parameters = len(parameters) + 1   907         return num_parameters - (defaults and len(defaults) or 0), num_parameters   908    909     def get_static_attributes(self, kind, name, attrnames):   910    911         """   912         Return a mapping of attribute names to paths for attributes belonging   913         to objects of the given 'kind' (being "<class>" or "<module>") with   914         the given 'name' and supporting the given 'attrnames'.   915         """   916    917         attrs = {}   918    919         for attrname in attrnames:   920             if attrname is None:   921                 continue   922             if kind == "<class>":   923                 path = self.importer.all_class_attrs[name][attrname]   924             elif kind == "<module>":   925                 path = "%s.%s" % (name, attrname)   926             else:   927                 continue   928    929             # The module may be hidden.   930    931             attr = self.importer.get_object(path)   932             if not attr:   933                 module = self.importer.hidden.get(path)   934                 if module:   935                     attr = Reference(module.name, "<module>")   936             attrs[attrname] = attr   937    938         return attrs   939    940     def get_instance_attributes(self, name, attrnames):   941    942         """   943         Return a mapping of attribute names to references for attributes   944         belonging to instances of the class with the given 'name', where the   945         given 'attrnames' are supported.   946         """   947    948         consts = self.importer.all_instance_attr_constants[name]   949         attrs = {}   950         for attrname in attrnames:   951             if attrname is None:   952                 continue   953             const = consts.get(attrname)   954             attrs[attrname] = const or Reference("<var>", "%s.%s" % (name, attrname))   955    956         # Instances with trailing data.   957    958         if name in self.trailing_data_types:   959             attrs["__trailing__"] = Reference("<var>", "%s.__trailing__" % name)   960    961         return attrs   962    963     def populate_table(self, path, table):   964    965         """   966         Traverse the attributes in the determined order for the structure having   967         the given 'path', adding entries to the attribute 'table'.   968         """   969    970         for attrname in self.optimiser.structures[path]:   971    972             # Handle gaps in the structure.   973    974             if attrname is None:   975                 table.append("0")   976             else:   977                 table.append(encode_code(attrname))   978    979     def populate_parameter_table(self, parameters, table):   980    981         """   982         Traverse the 'parameters' in the determined order, adding entries to the   983         attribute 'table'.   984         """   985    986         for value in parameters:   987    988             # Handle gaps in the structure.   989    990             if value is None:   991                 table.append(("0", "0"))   992             else:   993                 name, pos = value   994                 table.append((encode_symbol("pcode", name), pos))   995    996     def populate_function(self, path, function_instance_attrs):   997    998         """   999         Populate a structure for the function with the given 'path'. The given  1000         'attrs' provide the instance attributes.  1001         """  1002   1003         structure = []  1004         self.populate_structure(Reference("<function>", path), function_instance_attrs, "<instance>", structure)  1005   1006         # Append default members.  1007   1008         self.append_defaults(path, structure)  1009         return structure  1010   1011     def populate_structure(self, ref, attrs, kind, structure):  1012   1013         """  1014         Traverse the attributes in the determined order for the structure having  1015         the given 'ref' whose members are provided by the 'attrs' mapping, in a  1016         structure of the given 'kind', adding entries to the object 'structure'.  1017         """  1018   1019         structure_ref = self.get_target_structure(ref)  1020         origin = structure_ref.get_origin()  1021   1022         for attrname in self.optimiser.structures[structure_ref]:  1023   1024             # Handle gaps in the structure.  1025   1026             if attrname is None:  1027                 structure.append("__NULL")  1028   1029             # Handle non-constant and constant members.  1030   1031             else:  1032                 attr = attrs[attrname]  1033   1034                 # Special function pointer member.  1035   1036                 if attrname == "__fn__":  1037   1038                     # Classes offer instantiators which can be called without a  1039                     # context.  1040   1041                     if kind == "<class>":  1042                         attr = encode_instantiator_pointer(attr)  1043                     else:  1044                         attr = encode_function_pointer(attr)  1045   1046                     structure.append("{.fn=%s}" % attr)  1047                     continue  1048   1049                 # Special argument specification member.  1050   1051                 elif attrname == "__args__":  1052                     signature = self.get_signature_for_callable(ref.get_origin())  1053                     ptable = encode_tablename("<function>", signature)  1054   1055                     structure.append("{.ptable=&%s}" % ptable)  1056                     continue  1057   1058                 # Special internal data member.  1059   1060                 elif attrname == "__data__":  1061                     structure.append("{.%s=%s}" % (  1062                                      encode_literal_constant_member(attr),  1063                                      encode_literal_constant_value(attr)))  1064                     continue  1065   1066                 # Special internal size member.  1067   1068                 elif attrname == "__size__":  1069                     structure.append("__INTVALUE(%d)" % attr)  1070                     continue  1071   1072                 # Special internal key member.  1073   1074                 elif attrname == "__key__":  1075                     structure.append("{.code=%s, .pos=%s}" % (attr and encode_code(attr) or "0",  1076                                                               attr and encode_pos(attr) or "0"))  1077                     continue  1078   1079                 # Special cases.  1080   1081                 elif attrname in ("__file__", "__name__"):  1082                     path = ref.get_origin()  1083                     value_type = self.string_type  1084   1085                     # Provide constant values. These must match the values  1086                     # originally recorded during inspection.  1087   1088                     if attrname == "__file__":  1089                         module = self.importer.get_module(path)  1090                         value = module.filename  1091   1092                     # Function and class names are leafnames.  1093   1094                     elif attrname == "__name__" and not ref.has_kind("<module>"):  1095                         value = path.rsplit(".", 1)[-1]  1096   1097                     # All other names just use the object path information.  1098   1099                     else:  1100                         value = path  1101   1102                     encoding = None  1103   1104                     local_number = self.importer.all_constants[path][(value, value_type, encoding)]  1105                     constant_name = "$c%d" % local_number  1106                     attr_path = "%s.%s" % (path, constant_name)  1107                     constant_number = self.optimiser.constant_numbers[attr_path]  1108                     constant_value = "__const%s" % constant_number  1109                     structure.append("%s /* %s */" % (constant_value, attrname))  1110                     continue  1111   1112                 elif attrname == "__parent__":  1113                     path = ref.get_origin()  1114   1115                     # Parents of classes and functions are derived from their  1116                     # object paths.  1117   1118                     value = path.rsplit(".", 1)[0]  1119                     structure.append("{.value=&%s}" % encode_path(value))  1120                     continue  1121   1122                 # Special context member.  1123                 # Set the context depending on the kind of attribute.  1124                 # For methods:          <parent>  1125                 # For other attributes: __NULL  1126   1127                 elif attrname == "__context__":  1128                     path = ref.get_origin()  1129   1130                     # Contexts of methods are derived from their object paths.  1131   1132                     context = "0"  1133   1134                     if ref.get_kind() == "<function>":  1135                         parent = path.rsplit(".", 1)[0]  1136                         if self.importer.classes.has_key(parent):  1137                             context = "&%s" % encode_path(parent)  1138   1139                     structure.append("{.value=%s}" % context)  1140                     continue  1141   1142                 # Special class relationship attributes.  1143   1144                 elif is_type_attribute(attrname):  1145                     structure.append("{.value=&%s}" % encode_path(decode_type_attribute(attrname)))  1146                     continue  1147   1148                 # All other kinds of members.  1149   1150                 structure.append(self.encode_member(origin, attrname, attr, kind))  1151   1152     def populate_trailing(self, ref, attrs, trailing):  1153   1154         """  1155         For the structure having the given 'ref', whose members are provided by  1156         the 'attrs' mapping, adding entries to the 'trailing' member collection.  1157         """  1158   1159         structure_ref = self.get_target_structure(ref)  1160   1161         # Instances with trailing data.  1162   1163         if structure_ref.get_kind() == "<instance>" and \  1164            structure_ref.get_origin() in self.trailing_data_types:  1165             trailing.append(encode_literal_constant_value(attrs["__trailing__"]))  1166   1167     def get_target_structure(self, ref):  1168   1169         "Return the target structure type and reference for 'ref'."  1170   1171         # Populate function instance structures for functions.  1172   1173         if ref.has_kind("<function>"):  1174             return Reference("<instance>", self.function_type)  1175   1176         # Otherwise, just populate the appropriate structures.  1177   1178         else:  1179             return ref  1180   1181     def encode_member(self, path, name, ref, structure_type):  1182   1183         """  1184         Encode within the structure provided by 'path', the member whose 'name'  1185         provides 'ref', within the given 'structure_type'.  1186         """  1187   1188         kind = ref.get_kind()  1189         origin = ref.get_origin()  1190   1191         # References to constant literals.  1192   1193         if kind == "<instance>" and ref.is_constant_alias():  1194             alias = ref.get_name()  1195   1196             # Use the alias directly if appropriate.  1197   1198             if alias.startswith("$c"):  1199                 constant_value = encode_literal_constant(alias[2:])  1200                 return "%s /* %s */" % (constant_value, name)  1201   1202             # Obtain a constant value directly assigned to the attribute.  1203   1204             if self.optimiser.constant_numbers.has_key(alias):  1205   1206                 # Encode integer constants differently.  1207   1208                 value, value_type, encoding = self.importer.all_constant_values[alias]  1209                 if value_type == self.int_type:  1210                     return "__INTVALUE(%s) /* %s */" % (value, name)  1211   1212                 constant_number = self.optimiser.constant_numbers[alias]  1213                 constant_value = encode_literal_constant(constant_number)  1214                 return "%s /* %s */" % (constant_value, name)  1215   1216         # Usage of predefined constants, currently only None supported.  1217   1218         if kind == "<instance>" and origin == self.none_type:  1219             attr_path = encode_predefined_reference(self.none_value)  1220             return "{.value=(__ref) &%s} /* %s */" % (attr_path, name)  1221   1222         # Predefined constant members.  1223   1224         if (path, name) in self.predefined_constant_members:  1225             attr_path = encode_predefined_reference("%s.%s" % (path, name))  1226             return "{.value=(__ref) &%s} /* %s */" % (attr_path, name)  1227   1228         # General undetermined members.  1229   1230         if kind in ("<var>", "<instance>"):  1231             attr_path = encode_predefined_reference(self.none_value)  1232             return "{.value=(__ref) &%s} /* %s */" % (attr_path, name)  1233   1234         else:  1235             return "{.value=(__ref) &%s}" % encode_path(origin)  1236   1237     def append_defaults(self, path, structure):  1238   1239         """  1240         For the given 'path', append default parameter members to the given  1241         'structure'.  1242         """  1243   1244         for name, default in self.importer.function_defaults.get(path):  1245             structure.append(self.encode_member(path, name, default, "<instance>"))  1246   1247     def write_instantiator(self, f_code, f_signatures, path, init_ref):  1248   1249         """  1250         Write an instantiator to 'f_code', with a signature to 'f_signatures',  1251         for instances of the class with the given 'path', with 'init_ref' as the  1252         initialiser function reference.  1253   1254         NOTE: This also needs to initialise any __fn__ and __args__ members  1255         NOTE: where __call__ is provided by the class.  1256         """  1257   1258         initialiser = init_ref.get_origin()  1259         parameters = self.importer.function_parameters[initialiser]  1260         argmin, argmax = self.get_argument_limits(initialiser)  1261   1262         l = []  1263         for name in parameters:  1264             l.append("__attr %s" % name)  1265   1266         # Special-case the integer type.  1267   1268         # Here, the __builtins__.int.new_int function is called with the  1269         # initialiser's parameter.  1270   1271         if path == self.int_type:  1272             print >>f_code, """\  1273 __attr %s(__attr __self, __attr number_or_string, __attr base)  1274 {  1275     return __fn___builtins___int_new_int(__NULL, number_or_string, base);  1276 }  1277 """ % (  1278                 encode_instantiator_pointer(path),  1279                 )  1280   1281         # Generic instantiation support.  1282   1283         else:  1284             print >>f_code, """\  1285 __attr %s(__attr __self%s)  1286 {  1287     return %s(__NEWINSTANCE(%s)%s);  1288 }  1289 """ % (  1290                 encode_instantiator_pointer(path),  1291                 l and ", %s" % ",".join(l) or "",  1292                 encode_function_pointer(initialiser),  1293                 encode_path(path),  1294                 parameters and ", %s" % ", ".join(parameters) or ""  1295                 )  1296   1297         # Signature: __new_typename(__attr __self, ...)  1298   1299         print >>f_signatures, "__attr %s(__attr __self%s);" % (  1300             encode_instantiator_pointer(path),  1301             l and ", %s" % ", ".join(l) or ""  1302             )  1303   1304         print >>f_signatures, "#define __HAVE_%s" % encode_path(path)  1305   1306     def write_main_program(self, f_code, f_signatures):  1307   1308         """  1309         Write the main program to 'f_code', invoking the program's modules. Also  1310         write declarations for module main functions to 'f_signatures'.  1311         """  1312   1313         print >>f_code, """\  1314 int main(int argc, char *argv[])  1315 {  1316     __exc __tmp_exc;  1317   1318     GC_INIT();  1319   1320     __signals_install_handlers();  1321   1322     __Try  1323     {"""  1324   1325         for name in self.importer.order_modules():  1326             function_name = "__main_%s" % encode_path(name)  1327             print >>f_signatures, "void %s();" % function_name  1328   1329             # Omit the native modules.  1330   1331             parts = name.split(".")  1332   1333             if parts[0] != "native":  1334                 print >>f_code, """\  1335         %s();""" % function_name  1336   1337         print >>f_code, """\  1338     }  1339     __Catch(__tmp_exc)  1340     {  1341         if (__ISINSTANCE(__tmp_exc.arg, __ATTRVALUE(&__builtins___exception_system_SystemExit)))  1342             return __TOINT(__load_via_object(__VALUE(__tmp_exc.arg), value));  1343   1344         fprintf(stderr, "Program terminated due to exception: %%s.\\n",  1345                 __load_via_object(  1346                     __VALUE(%s(__NULL, __tmp_exc.arg)),  1347                     __data__).strvalue);  1348         return 1;  1349     }  1350   1351     return 0;  1352 }  1353 """ % encode_function_pointer("__builtins__.str.str")  1354   1355 # vim: tabstop=4 expandtab shiftwidth=4