Lichen

Annotated generator.py

336:8c75cdf1a764
2016-12-06 Paul Boddie Introduced stream classes employing C-level FILE pointers, changing the sys stdin, stdout and stderr objects to be instances of these stream classes. Added fread and fwrite support to the native functions. Added support for raising EOFError.
paul@126 1
#!/usr/bin/env python
paul@126 2
paul@126 3
"""
paul@126 4
Generate C code from object layouts and other deduced information.
paul@126 5
paul@126 6
Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk>
paul@126 7
paul@126 8
This program is free software; you can redistribute it and/or modify it under
paul@126 9
the terms of the GNU General Public License as published by the Free Software
paul@126 10
Foundation; either version 3 of the License, or (at your option) any later
paul@126 11
version.
paul@126 12
paul@126 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@126 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@126 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@126 16
details.
paul@126 17
paul@126 18
You should have received a copy of the GNU General Public License along with
paul@126 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@126 20
"""
paul@126 21
paul@126 22
from common import CommonOutput
paul@126 23
from encoders import encode_bound_reference, encode_function_pointer, \
paul@136 24
                     encode_instantiator_pointer, \
paul@136 25
                     encode_literal_constant, encode_literal_constant_member, \
paul@159 26
                     encode_literal_constant_value, \
paul@283 27
                     encode_literal_data_initialiser, \
paul@159 28
                     encode_literal_instantiator, encode_literal_reference, \
paul@136 29
                     encode_path, \
paul@150 30
                     encode_predefined_reference, encode_size, \
paul@150 31
                     encode_symbol, encode_tablename, \
paul@318 32
                     encode_type_attribute, decode_type_attribute, \
paul@318 33
                     is_type_attribute
paul@126 34
from os import listdir
paul@183 35
from os.path import exists, isdir, join, split
paul@126 36
from referencing import Reference
paul@126 37
paul@126 38
def copy(source, target):
paul@126 39
paul@126 40
    "Copy a text file from 'source' to 'target'."
paul@126 41
paul@126 42
    if isdir(target):
paul@126 43
        target = join(target, split(source)[-1])
paul@126 44
    infile = open(source)
paul@126 45
    outfile = open(target, "w")
paul@126 46
    try:
paul@126 47
        outfile.write(infile.read())
paul@126 48
    finally:
paul@126 49
        outfile.close()
paul@126 50
        infile.close()
paul@126 51
paul@126 52
class Generator(CommonOutput):
paul@126 53
paul@126 54
    "A code generator."
paul@126 55
paul@274 56
    # NOTE: These must be synchronised with the library.
paul@274 57
paul@126 58
    function_type = "__builtins__.core.function"
paul@274 59
    type_type = "__builtins__.core.type"
paul@158 60
paul@136 61
    predefined_constant_members = (
paul@158 62
        ("__builtins__.boolean", "False"),
paul@158 63
        ("__builtins__.boolean", "True"),
paul@136 64
        ("__builtins__.none", "None"),
paul@136 65
        ("__builtins__.notimplemented", "NotImplemented"),
paul@136 66
        )
paul@136 67
paul@283 68
    literal_mapping_types = (
paul@159 69
        "__builtins__.dict.dict",
paul@283 70
        )
paul@283 71
paul@283 72
    literal_sequence_types = (
paul@159 73
        "__builtins__.list.list",
paul@159 74
        "__builtins__.tuple.tuple",
paul@159 75
        )
paul@159 76
paul@283 77
    literal_instantiator_types = literal_mapping_types + literal_sequence_types
paul@283 78
paul@126 79
    def __init__(self, importer, optimiser, output):
paul@126 80
        self.importer = importer
paul@126 81
        self.optimiser = optimiser
paul@126 82
        self.output = output
paul@126 83
paul@183 84
    def to_output(self, debug=False):
paul@126 85
paul@126 86
        "Write the generated code."
paul@126 87
paul@126 88
        self.check_output()
paul@126 89
        self.write_structures()
paul@183 90
        self.copy_templates(debug)
paul@126 91
paul@183 92
    def copy_templates(self, debug=False):
paul@126 93
paul@126 94
        "Copy template files to the generated output directory."
paul@126 95
paul@126 96
        templates = join(split(__file__)[0], "templates")
paul@126 97
paul@126 98
        for filename in listdir(templates):
paul@183 99
            target = self.output
paul@183 100
paul@183 101
            # Handle debug resources.
paul@183 102
paul@183 103
            if filename.endswith("-debug"):
paul@183 104
                if debug:
paul@183 105
                    target = join(self.output, filename[:-len("-debug")])
paul@183 106
                else:
paul@183 107
                    continue
paul@183 108
paul@183 109
            # Handle non-debug resources.
paul@183 110
paul@183 111
            if debug and exists(join(templates, "%s-debug" % filename)):
paul@183 112
                continue
paul@183 113
paul@183 114
            copy(join(templates, filename), target)
paul@126 115
paul@126 116
    def write_structures(self):
paul@126 117
paul@126 118
        "Write structures used by the program."
paul@126 119
paul@126 120
        f_consts = open(join(self.output, "progconsts.h"), "w")
paul@126 121
        f_defs = open(join(self.output, "progtypes.c"), "w")
paul@126 122
        f_decls = open(join(self.output, "progtypes.h"), "w")
paul@126 123
        f_signatures = open(join(self.output, "main.h"), "w")
paul@126 124
        f_code = open(join(self.output, "main.c"), "w")
paul@126 125
paul@126 126
        try:
paul@126 127
            # Output boilerplate.
paul@126 128
paul@126 129
            print >>f_consts, """\
paul@126 130
#ifndef __PROGCONSTS_H__
paul@126 131
#define __PROGCONSTS_H__
paul@126 132
"""
paul@126 133
            print >>f_decls, """\
paul@126 134
#ifndef __PROGTYPES_H__
paul@126 135
#define __PROGTYPES_H__
paul@126 136
paul@126 137
#include "progconsts.h"
paul@126 138
#include "types.h"
paul@126 139
"""
paul@126 140
            print >>f_defs, """\
paul@126 141
#include "progtypes.h"
paul@132 142
#include "progops.h"
paul@126 143
#include "main.h"
paul@126 144
"""
paul@126 145
            print >>f_signatures, """\
paul@126 146
#ifndef __MAIN_H__
paul@126 147
#define __MAIN_H__
paul@126 148
paul@126 149
#include "types.h"
paul@126 150
"""
paul@126 151
            print >>f_code, """\
paul@126 152
#include <string.h>
paul@159 153
#include <stdio.h>
paul@126 154
#include "types.h"
paul@159 155
#include "exceptions.h"
paul@126 156
#include "ops.h"
paul@126 157
#include "progconsts.h"
paul@126 158
#include "progtypes.h"
paul@126 159
#include "progops.h"
paul@126 160
#include "main.h"
paul@126 161
"""
paul@126 162
paul@126 163
            # Generate table and structure data.
paul@126 164
paul@126 165
            function_instance_attrs = None
paul@126 166
            objects = self.optimiser.attr_table.items()
paul@126 167
            objects.sort()
paul@126 168
paul@126 169
            for ref, indexes in objects:
paul@126 170
                attrnames = self.get_attribute_names(indexes)
paul@126 171
paul@126 172
                kind = ref.get_kind()
paul@126 173
                path = ref.get_origin()
paul@150 174
                table_name = encode_tablename(kind, path)
paul@150 175
                structure_size = encode_size(kind, path)
paul@126 176
paul@126 177
                # Generate structures for classes and modules.
paul@126 178
paul@126 179
                if kind != "<instance>":
paul@126 180
                    structure = []
paul@126 181
                    attrs = self.get_static_attributes(kind, path, attrnames)
paul@126 182
paul@126 183
                    # Set a special instantiator on the class.
paul@126 184
paul@126 185
                    if kind == "<class>":
paul@126 186
                        attrs["__fn__"] = path
paul@126 187
                        attrs["__args__"] = encode_size("pmin", path)
paul@126 188
paul@126 189
                        # Write instantiator declarations based on the
paul@126 190
                        # applicable initialiser.
paul@126 191
paul@126 192
                        init_ref = attrs["__init__"]
paul@126 193
paul@126 194
                        # Write instantiator definitions.
paul@126 195
paul@159 196
                        self.write_instantiator(f_code, f_signatures, path, init_ref)
paul@126 197
paul@126 198
                        # Write parameter table.
paul@126 199
paul@126 200
                        self.make_parameter_table(f_decls, f_defs, path, init_ref.get_origin())
paul@126 201
paul@126 202
                    self.populate_structure(Reference(kind, path), attrs, kind, structure)
paul@136 203
paul@136 204
                    if kind == "<class>":
paul@136 205
                        self.write_instance_structure(f_decls, path, structure_size)
paul@136 206
paul@211 207
                    self.write_structure(f_decls, f_defs, path, table_name, structure,
paul@136 208
                        kind == "<class>" and path)
paul@126 209
paul@126 210
                # Record function instance details for function generation below.
paul@126 211
paul@126 212
                else:
paul@126 213
                    attrs = self.get_instance_attributes(path, attrnames)
paul@126 214
                    if path == self.function_type:
paul@126 215
                        function_instance_attrs = attrs
paul@126 216
paul@126 217
                # Write a table for all objects.
paul@126 218
paul@126 219
                table = []
paul@126 220
                self.populate_table(Reference(kind, path), table)
paul@126 221
                self.write_table(f_decls, f_defs, table_name, structure_size, table)
paul@126 222
paul@126 223
            # Generate function instances.
paul@126 224
paul@195 225
            functions = self.importer.function_parameters.keys()
paul@126 226
            functions.sort()
paul@211 227
            extra_function_instances = []
paul@126 228
paul@126 229
            for path in functions:
paul@195 230
paul@195 231
                # Instantiators are generated above.
paul@195 232
paul@195 233
                if self.importer.classes.has_key(path) or not self.importer.get_object(path):
paul@195 234
                    continue
paul@195 235
paul@126 236
                cls = self.function_type
paul@150 237
                table_name = encode_tablename("<instance>", cls)
paul@211 238
                structure_size = encode_size("<instance>", path)
paul@126 239
paul@126 240
                # Set a special callable attribute on the instance.
paul@126 241
paul@126 242
                function_instance_attrs["__fn__"] = path
paul@126 243
                function_instance_attrs["__args__"] = encode_size("pmin", path)
paul@126 244
paul@126 245
                # Produce two structures where a method is involved.
paul@126 246
paul@195 247
                parent, name = path.rsplit(".", 1)
paul@195 248
                parent_ref = self.importer.get_object(parent)
paul@126 249
                parent_kind = parent_ref and parent_ref.get_kind()
paul@126 250
paul@126 251
                # Populate and write each structure.
paul@126 252
paul@126 253
                if parent_kind == "<class>":
paul@126 254
paul@132 255
                    # A bound version of a method.
paul@132 256
paul@132 257
                    structure = self.populate_function(path, function_instance_attrs, False)
paul@211 258
                    self.write_structure(f_decls, f_defs, encode_bound_reference(path), table_name, structure)
paul@132 259
paul@126 260
                    # An unbound version of a method.
paul@126 261
paul@126 262
                    structure = self.populate_function(path, function_instance_attrs, True)
paul@211 263
                    self.write_structure(f_decls, f_defs, path, table_name, structure)
paul@126 264
paul@132 265
                else:
paul@132 266
                    # A normal function.
paul@126 267
paul@126 268
                    structure = self.populate_function(path, function_instance_attrs, False)
paul@211 269
                    self.write_structure(f_decls, f_defs, path, table_name, structure)
paul@126 270
paul@154 271
                # Functions with defaults need to declare instance structures.
paul@154 272
paul@154 273
                if self.importer.function_defaults.get(path):
paul@154 274
                    self.write_instance_structure(f_decls, path, structure_size)
paul@211 275
                    extra_function_instances.append(path)
paul@154 276
paul@126 277
                # Write function declarations.
paul@126 278
                # Signature: __attr <name>(__attr[]);
paul@126 279
paul@126 280
                print >>f_signatures, "__attr %s(__attr args[]);" % encode_function_pointer(path)
paul@126 281
paul@126 282
                # Write parameter table.
paul@126 283
paul@126 284
                self.make_parameter_table(f_decls, f_defs, path, path)
paul@126 285
paul@136 286
            # Generate predefined constants.
paul@136 287
paul@136 288
            for path, name in self.predefined_constant_members:
paul@136 289
                self.make_predefined_constant(f_decls, f_defs, path, name)
paul@136 290
paul@136 291
            # Generate literal constants.
paul@136 292
paul@136 293
            for value, n in self.optimiser.constants.items():
paul@136 294
                self.make_literal_constant(f_decls, f_defs, n, value)
paul@136 295
paul@146 296
            # Finish the main source file.
paul@146 297
paul@146 298
            self.write_main_program(f_code, f_signatures)
paul@146 299
paul@211 300
            # Record size information for certain function instances as well as
paul@211 301
            # for classes, modules and other instances.
paul@211 302
paul@211 303
            size_tables = {}
paul@211 304
paul@211 305
            for kind in ["<class>", "<module>", "<instance>"]:
paul@211 306
                size_tables[kind] = {}
paul@211 307
paul@211 308
            # Generate structure size data.
paul@211 309
paul@211 310
            for ref, structure in self.optimiser.structures.items():
paul@211 311
                size_tables[ref.get_kind()][ref.get_origin()] = len(structure)
paul@211 312
paul@211 313
            for path in extra_function_instances:
paul@211 314
                defaults = self.importer.function_defaults[path]
paul@211 315
                size_tables["<instance>"][path] = size_tables["<instance>"][self.function_type] + len(defaults)
paul@211 316
paul@211 317
            size_tables = size_tables.items()
paul@211 318
            size_tables.sort()
paul@211 319
paul@211 320
            for kind, sizes in size_tables:
paul@211 321
                self.write_size_constants(f_consts, kind, sizes, 0)
paul@211 322
paul@211 323
            # Generate parameter table size data.
paul@211 324
paul@211 325
            min_sizes = {}
paul@211 326
            max_sizes = {}
paul@211 327
paul@211 328
            for path, parameters in self.optimiser.parameters.items():
paul@211 329
                argmin, argmax = self.get_argument_limits(path)
paul@211 330
                min_sizes[path] = argmin
paul@211 331
                max_sizes[path] = argmax
paul@211 332
paul@211 333
                # Record instantiator limits.
paul@211 334
paul@211 335
                if path.endswith(".__init__"):
paul@211 336
                    path = path[:-len(".__init__")]
paul@211 337
paul@211 338
            self.write_size_constants(f_consts, "pmin", min_sizes, 0)
paul@211 339
            self.write_size_constants(f_consts, "pmax", max_sizes, 0)
paul@211 340
paul@211 341
            # Generate parameter codes.
paul@211 342
paul@211 343
            self.write_code_constants(f_consts, self.optimiser.all_paramnames, self.optimiser.arg_locations, "pcode", "ppos")
paul@211 344
paul@211 345
            # Generate attribute codes.
paul@211 346
paul@211 347
            self.write_code_constants(f_consts, self.optimiser.all_attrnames, self.optimiser.locations, "code", "pos")
paul@211 348
paul@126 349
            # Output more boilerplate.
paul@126 350
paul@126 351
            print >>f_consts, """\
paul@126 352
paul@126 353
#endif /* __PROGCONSTS_H__ */"""
paul@126 354
paul@126 355
            print >>f_decls, """\
paul@126 356
paul@126 357
#define __FUNCTION_TYPE %s
paul@126 358
#define __FUNCTION_INSTANCE_SIZE %s
paul@274 359
#define __TYPE_CLASS_TYPE %s
paul@274 360
#define __TYPE_CLASS_POS %s
paul@274 361
#define __TYPE_CLASS_CODE %s
paul@126 362
paul@126 363
#endif /* __PROGTYPES_H__ */""" % (
paul@126 364
    encode_path(self.function_type),
paul@233 365
    encode_size("<instance>", self.function_type),
paul@274 366
    encode_path(self.type_type),
paul@274 367
    encode_symbol("pos", encode_type_attribute(self.type_type)),
paul@274 368
    encode_symbol("code", encode_type_attribute(self.type_type)),
paul@126 369
    )
paul@126 370
paul@126 371
            print >>f_signatures, """\
paul@126 372
paul@126 373
#endif /* __MAIN_H__ */"""
paul@126 374
paul@126 375
        finally:
paul@126 376
            f_consts.close()
paul@126 377
            f_defs.close()
paul@126 378
            f_decls.close()
paul@126 379
            f_signatures.close()
paul@126 380
            f_code.close()
paul@126 381
paul@136 382
    def make_literal_constant(self, f_decls, f_defs, n, value):
paul@136 383
paul@136 384
        """
paul@136 385
        Write literal constant details to 'f_decls' (to declare a structure) and
paul@136 386
        to 'f_defs' (to define the contents) for the constant with the number
paul@136 387
        'n' with the given literal 'value'.
paul@136 388
        """
paul@136 389
paul@136 390
        const_path = encode_literal_constant(n)
paul@136 391
        structure_name = encode_literal_reference(n)
paul@136 392
paul@136 393
        # NOTE: This makes assumptions about the __builtins__ structure.
paul@136 394
paul@188 395
        modname = value.__class__.__name__
paul@188 396
        typename = modname == "str" and "string" or modname
paul@188 397
        ref = Reference("<instance>", "__builtins__.%s.%s" % (modname, typename))
paul@136 398
paul@136 399
        self.make_constant(f_decls, f_defs, ref, const_path, structure_name, value)
paul@136 400
paul@136 401
    def make_predefined_constant(self, f_decls, f_defs, path, name):
paul@136 402
paul@136 403
        """
paul@136 404
        Write predefined constant details to 'f_decls' (to declare a structure)
paul@136 405
        and to 'f_defs' (to define the contents) for the constant located in
paul@136 406
        'path' with the given 'name'.
paul@136 407
        """
paul@136 408
paul@136 409
        # Determine the details of the constant.
paul@136 410
paul@136 411
        attr_path = "%s.%s" % (path, name)
paul@136 412
        structure_name = encode_predefined_reference(attr_path)
paul@136 413
        ref = self.importer.get_object(attr_path)
paul@136 414
paul@136 415
        self.make_constant(f_decls, f_defs, ref, attr_path, structure_name)
paul@136 416
paul@136 417
    def make_constant(self, f_decls, f_defs, ref, const_path, structure_name, data=None):
paul@136 418
paul@136 419
        """
paul@136 420
        Write constant details to 'f_decls' (to declare a structure) and to
paul@136 421
        'f_defs' (to define the contents) for the constant described by 'ref'
paul@136 422
        having the given 'path' and 'structure_name' (for the constant structure
paul@136 423
        itself).
paul@136 424
        """
paul@136 425
paul@136 426
        # Obtain the attributes.
paul@136 427
paul@136 428
        cls = ref.get_origin()
paul@136 429
        indexes = self.optimiser.attr_table[ref]
paul@136 430
        attrnames = self.get_attribute_names(indexes)
paul@136 431
        attrs = self.get_instance_attributes(cls, attrnames)
paul@136 432
paul@136 433
        # Set the data, if provided.
paul@136 434
paul@136 435
        if data is not None:
paul@136 436
            attrs["__data__"] = data
paul@136 437
paul@136 438
        # Define the structure details. An object is created for the constant,
paul@136 439
        # but an attribute is provided, referring to the object, for access to
paul@136 440
        # the constant in the program.
paul@136 441
paul@136 442
        structure = []
paul@150 443
        table_name = encode_tablename("<instance>", cls)
paul@136 444
        self.populate_structure(ref, attrs, ref.get_kind(), structure)
paul@211 445
        self.write_structure(f_decls, f_defs, structure_name, table_name, structure)
paul@136 446
paul@136 447
        # Define a macro for the constant.
paul@136 448
paul@136 449
        attr_name = encode_path(const_path)
paul@136 450
        print >>f_decls, "#define %s ((__attr) {&%s, &%s})" % (attr_name, structure_name, structure_name)
paul@136 451
paul@126 452
    def make_parameter_table(self, f_decls, f_defs, path, function_path):
paul@126 453
paul@126 454
        """
paul@126 455
        Write parameter table details to 'f_decls' (to declare a table) and to
paul@126 456
        'f_defs' (to define the contents) for the function with the given
paul@126 457
        'path', using 'function_path' to obtain the parameter details. The
paul@126 458
        latter two arguments may differ when describing an instantiator using
paul@126 459
        the details of an initialiser.
paul@126 460
        """
paul@126 461
paul@126 462
        table = []
paul@150 463
        table_name = encode_tablename("<function>", path)
paul@126 464
        structure_size = encode_size("pmax", path)
paul@126 465
        self.populate_parameter_table(function_path, table)
paul@126 466
        self.write_parameter_table(f_decls, f_defs, table_name, structure_size, table)
paul@126 467
paul@126 468
    def write_size_constants(self, f_consts, size_prefix, sizes, padding):
paul@126 469
paul@126 470
        """
paul@126 471
        Write size constants to 'f_consts' for the given 'size_prefix', using
paul@126 472
        the 'sizes' dictionary to populate the definition, adding the given
paul@126 473
        'padding' to the basic sizes.
paul@126 474
        """
paul@126 475
paul@126 476
        print >>f_consts, "enum %s {" % encode_size(size_prefix)
paul@126 477
        first = True
paul@126 478
        for path, size in sizes.items():
paul@126 479
            if not first:
paul@126 480
                print >>f_consts, ","
paul@126 481
            else:
paul@126 482
                first = False
paul@126 483
            f_consts.write("    %s = %d" % (encode_size(size_prefix, path), size + padding))
paul@126 484
        print >>f_consts, "\n    };"
paul@126 485
paul@132 486
    def write_code_constants(self, f_consts, attrnames, locations, code_prefix, pos_prefix):
paul@126 487
paul@126 488
        """
paul@126 489
        Write code constants to 'f_consts' for the given 'attrnames' and
paul@126 490
        attribute 'locations'.
paul@126 491
        """
paul@126 492
paul@132 493
        print >>f_consts, "enum %s {" % encode_symbol(code_prefix)
paul@126 494
        first = True
paul@126 495
        for i, attrname in enumerate(attrnames):
paul@126 496
            if not first:
paul@126 497
                print >>f_consts, ","
paul@126 498
            else:
paul@126 499
                first = False
paul@132 500
            f_consts.write("    %s = %d" % (encode_symbol(code_prefix, attrname), i))
paul@126 501
        print >>f_consts, "\n    };"
paul@126 502
paul@132 503
        print >>f_consts, "enum %s {" % encode_symbol(pos_prefix)
paul@126 504
        first = True
paul@126 505
        for i, attrnames in enumerate(locations):
paul@126 506
            for attrname in attrnames:
paul@126 507
                if not first:
paul@126 508
                    print >>f_consts, ","
paul@126 509
                else:
paul@126 510
                    first = False
paul@132 511
                f_consts.write("    %s = %d" % (encode_symbol(pos_prefix, attrname), i))
paul@126 512
        print >>f_consts, "\n    };"
paul@126 513
paul@126 514
    def write_table(self, f_decls, f_defs, table_name, structure_size, table):
paul@126 515
paul@126 516
        """
paul@126 517
        Write the declarations to 'f_decls' and definitions to 'f_defs' for
paul@126 518
        the object having the given 'table_name' and the given 'structure_size',
paul@126 519
        with 'table' details used to populate the definition.
paul@126 520
        """
paul@126 521
paul@126 522
        print >>f_decls, "extern const __table %s;\n" % table_name
paul@126 523
paul@126 524
        # Write the corresponding definition.
paul@126 525
paul@126 526
        print >>f_defs, "const __table %s = {\n    %s,\n    {\n        %s\n        }\n    };\n" % (
paul@126 527
            table_name, structure_size,
paul@126 528
            ",\n        ".join(table))
paul@126 529
paul@126 530
    def write_parameter_table(self, f_decls, f_defs, table_name, structure_size, table):
paul@126 531
paul@126 532
        """
paul@126 533
        Write the declarations to 'f_decls' and definitions to 'f_defs' for
paul@126 534
        the object having the given 'table_name' and the given 'structure_size',
paul@126 535
        with 'table' details used to populate the definition.
paul@126 536
        """
paul@126 537
paul@126 538
        print >>f_decls, "extern const __ptable %s;\n" % table_name
paul@126 539
paul@126 540
        # Write the corresponding definition.
paul@126 541
paul@126 542
        print >>f_defs, "const __ptable %s = {\n    %s,\n    {\n        %s\n        }\n    };\n" % (
paul@126 543
            table_name, structure_size,
paul@126 544
            ",\n        ".join([("{%s, %s}" % t) for t in table]))
paul@126 545
paul@136 546
    def write_instance_structure(self, f_decls, path, structure_size):
paul@126 547
paul@126 548
        """
paul@136 549
        Write a declaration to 'f_decls' for the object having the given 'path'
paul@136 550
        and the given 'structure_size'.
paul@126 551
        """
paul@126 552
paul@126 553
        # Write an instance-specific type definition for instances of classes.
paul@126 554
        # See: templates/types.h
paul@126 555
paul@126 556
        print >>f_decls, """\
paul@126 557
typedef struct {
paul@126 558
    const __table * table;
paul@126 559
    unsigned int pos;
paul@126 560
    __attr attrs[%s];
paul@126 561
} %s;
paul@136 562
""" % (structure_size, encode_symbol("obj", path))
paul@136 563
paul@211 564
    def write_structure(self, f_decls, f_defs, structure_name, table_name, structure, path=None):
paul@126 565
paul@136 566
        """
paul@136 567
        Write the declarations to 'f_decls' and definitions to 'f_defs' for
paul@136 568
        the object having the given 'structure_name', the given 'table_name',
paul@211 569
        and the given 'structure' details used to populate the definition.
paul@136 570
        """
paul@126 571
paul@136 572
        if f_decls:
paul@136 573
            print >>f_decls, "extern __obj %s;\n" % encode_path(structure_name)
paul@136 574
paul@136 575
        is_class = path and self.importer.get_object(path).has_kind("<class>")
paul@132 576
        pos = is_class and encode_symbol("pos", encode_type_attribute(path)) or "0"
paul@132 577
paul@132 578
        print >>f_defs, """\
paul@132 579
__obj %s = {
paul@132 580
    &%s,
paul@132 581
    %s,
paul@132 582
    {
paul@132 583
        %s
paul@132 584
    }};
paul@132 585
""" % (
paul@136 586
            encode_path(structure_name), table_name, pos,
paul@126 587
            ",\n        ".join(structure))
paul@126 588
paul@132 589
    def get_argument_limits(self, path):
paul@126 590
paul@132 591
        """
paul@132 592
        Return the argument minimum and maximum for the callable at 'path',
paul@132 593
        adding an argument position for a universal context.
paul@132 594
        """
paul@132 595
paul@126 596
        parameters = self.importer.function_parameters[path]
paul@126 597
        defaults = self.importer.function_defaults.get(path)
paul@132 598
        num_parameters = len(parameters) + 1
paul@132 599
        return num_parameters - (defaults and len(defaults) or 0), num_parameters
paul@126 600
paul@126 601
    def get_attribute_names(self, indexes):
paul@126 602
paul@126 603
        """
paul@126 604
        Given a list of attribute table 'indexes', return a list of attribute
paul@126 605
        names.
paul@126 606
        """
paul@126 607
paul@126 608
        all_attrnames = self.optimiser.all_attrnames
paul@126 609
        attrnames = []
paul@126 610
        for i in indexes:
paul@126 611
            if i is None:
paul@126 612
                attrnames.append(None)
paul@126 613
            else:
paul@126 614
                attrnames.append(all_attrnames[i])
paul@126 615
        return attrnames
paul@126 616
paul@126 617
    def get_static_attributes(self, kind, name, attrnames):
paul@126 618
paul@126 619
        """
paul@126 620
        Return a mapping of attribute names to paths for attributes belonging
paul@126 621
        to objects of the given 'kind' (being "<class>" or "<module>") with
paul@126 622
        the given 'name' and supporting the given 'attrnames'.
paul@126 623
        """
paul@126 624
paul@126 625
        attrs = {}
paul@126 626
paul@126 627
        for attrname in attrnames:
paul@126 628
            if attrname is None:
paul@126 629
                continue
paul@126 630
            if kind == "<class>":
paul@126 631
                path = self.importer.all_class_attrs[name][attrname]
paul@126 632
            elif kind == "<module>":
paul@126 633
                path = "%s.%s" % (name, attrname)
paul@126 634
            else:
paul@126 635
                continue
paul@126 636
paul@126 637
            # The module may be hidden.
paul@126 638
paul@126 639
            attr = self.importer.get_object(path)
paul@126 640
            if not attr:
paul@126 641
                module = self.importer.hidden.get(path)
paul@126 642
                if module:
paul@126 643
                    attr = Reference(module.name, "<module>")
paul@126 644
            attrs[attrname] = attr
paul@126 645
paul@126 646
        return attrs
paul@126 647
paul@126 648
    def get_instance_attributes(self, name, attrnames):
paul@126 649
paul@126 650
        """
paul@126 651
        Return a mapping of attribute names to references for attributes
paul@126 652
        belonging to instances of the class with the given 'name', where the
paul@126 653
        given 'attrnames' are supported.
paul@126 654
        """
paul@126 655
paul@126 656
        consts = self.importer.all_instance_attr_constants[name]
paul@126 657
        attrs = {}
paul@126 658
        for attrname in attrnames:
paul@126 659
            if attrname is None:
paul@126 660
                continue
paul@126 661
            const = consts.get(attrname)
paul@126 662
            attrs[attrname] = const or Reference("<var>", "%s.%s" % (name, attrname))
paul@126 663
        return attrs
paul@126 664
paul@126 665
    def populate_table(self, key, table):
paul@126 666
paul@126 667
        """
paul@126 668
        Traverse the attributes in the determined order for the structure having
paul@126 669
        the given 'key', adding entries to the attribute 'table'.
paul@126 670
        """
paul@126 671
paul@126 672
        for attrname in self.optimiser.structures[key]:
paul@126 673
paul@126 674
            # Handle gaps in the structure.
paul@126 675
paul@126 676
            if attrname is None:
paul@126 677
                table.append("0")
paul@126 678
            else:
paul@126 679
                table.append(encode_symbol("code", attrname))
paul@126 680
paul@126 681
    def populate_parameter_table(self, key, table):
paul@126 682
paul@126 683
        """
paul@126 684
        Traverse the parameters in the determined order for the structure having
paul@126 685
        the given 'key', adding entries to the attribute 'table'.
paul@126 686
        """
paul@126 687
paul@126 688
        for value in self.optimiser.parameters[key]:
paul@126 689
paul@126 690
            # Handle gaps in the structure.
paul@126 691
paul@126 692
            if value is None:
paul@126 693
                table.append(("0", "0"))
paul@126 694
            else:
paul@126 695
                name, pos = value
paul@126 696
                table.append((encode_symbol("pcode", name), pos))
paul@126 697
paul@126 698
    def populate_function(self, path, function_instance_attrs, unbound=False):
paul@126 699
paul@126 700
        """
paul@126 701
        Populate a structure for the function with the given 'path'. The given
paul@126 702
        'attrs' provide the instance attributes, and if 'unbound' is set to a
paul@126 703
        true value, an unbound method structure is produced (as opposed to a
paul@126 704
        callable bound method structure).
paul@126 705
        """
paul@126 706
paul@126 707
        structure = []
paul@174 708
        self.populate_structure(Reference("<function>", path), function_instance_attrs, "<instance>", structure, unbound)
paul@126 709
paul@126 710
        # Append default members.
paul@126 711
paul@126 712
        self.append_defaults(path, structure)
paul@126 713
        return structure
paul@126 714
paul@126 715
    def populate_structure(self, ref, attrs, kind, structure, unbound=False):
paul@126 716
paul@126 717
        """
paul@126 718
        Traverse the attributes in the determined order for the structure having
paul@126 719
        the given 'ref' whose members are provided by the 'attrs' mapping, in a
paul@126 720
        structure of the given 'kind', adding entries to the object 'structure'.
paul@126 721
        If 'unbound' is set to a true value, an unbound method function pointer
paul@126 722
        will be employed, with a reference to the bound method incorporated into
paul@126 723
        the special __fn__ attribute.
paul@126 724
        """
paul@126 725
paul@174 726
        # Populate function instance structures for functions.
paul@174 727
paul@174 728
        if ref.has_kind("<function>"):
paul@174 729
            origin = self.function_type
paul@174 730
            structure_ref = Reference("<instance>", self.function_type)
paul@174 731
paul@174 732
        # Otherwise, just populate the appropriate structures.
paul@126 733
paul@174 734
        else:
paul@174 735
            origin = ref.get_origin()
paul@174 736
            structure_ref = ref
paul@174 737
paul@174 738
        # Refer to instantiator function tables for classes, specific function
paul@174 739
        # tables for individual functions.
paul@174 740
paul@174 741
        ptable = encode_tablename("<function>", ref.get_origin())
paul@174 742
paul@174 743
        for attrname in self.optimiser.structures[structure_ref]:
paul@126 744
paul@126 745
            # Handle gaps in the structure.
paul@126 746
paul@126 747
            if attrname is None:
paul@126 748
                structure.append("{0, 0}")
paul@126 749
paul@126 750
            # Handle non-constant and constant members.
paul@126 751
paul@126 752
            else:
paul@126 753
                attr = attrs[attrname]
paul@126 754
paul@136 755
                # Special function pointer member.
paul@136 756
paul@126 757
                if attrname == "__fn__":
paul@126 758
paul@126 759
                    # Provide bound method references and the unbound function
paul@126 760
                    # pointer if populating methods in a class.
paul@126 761
paul@126 762
                    bound_attr = None
paul@126 763
paul@126 764
                    # Classes offer instantiators.
paul@126 765
paul@126 766
                    if kind == "<class>":
paul@126 767
                        attr = encode_instantiator_pointer(attr)
paul@126 768
paul@126 769
                    # Methods offers references to bound versions and an unbound
paul@126 770
                    # method function.
paul@126 771
paul@126 772
                    elif unbound:
paul@126 773
                        bound_attr = encode_bound_reference(attr)
paul@126 774
                        attr = "__unbound_method"
paul@126 775
paul@126 776
                    # Other functions just offer function pointers.
paul@126 777
paul@126 778
                    else:
paul@126 779
                        attr = encode_function_pointer(attr)
paul@126 780
paul@132 781
                    structure.append("{%s, .fn=%s}" % (bound_attr and ".b=&%s" % bound_attr or "0", attr))
paul@126 782
                    continue
paul@126 783
paul@136 784
                # Special argument specification member.
paul@136 785
paul@126 786
                elif attrname == "__args__":
paul@174 787
                    structure.append("{.min=%s, .ptable=&%s}" % (attr, ptable))
paul@126 788
                    continue
paul@126 789
paul@136 790
                # Special internal data member.
paul@136 791
paul@136 792
                elif attrname == "__data__":
paul@136 793
                    structure.append("{0, .%s=%s}" % (encode_literal_constant_member(attr),
paul@136 794
                                                      encode_literal_constant_value(attr)))
paul@136 795
                    continue
paul@136 796
paul@251 797
                # Special cases.
paul@251 798
paul@271 799
                elif attrname in ("__file__", "__fname__",  "__mname__", "__name__"):
paul@251 800
                    path = ref.get_origin()
paul@271 801
paul@271 802
                    if attrname == "__file__":
paul@271 803
                        module = self.importer.get_module(path)
paul@271 804
                        value = module.filename
paul@271 805
                    else:
paul@271 806
                        value = path
paul@271 807
paul@271 808
                    local_number = self.importer.all_constants[path][value]
paul@251 809
                    constant_name = "$c%d" % local_number
paul@251 810
                    attr_path = "%s.%s" % (path, constant_name)
paul@251 811
                    constant_number = self.optimiser.constant_numbers[attr_path]
paul@251 812
                    constant_value = "__const%d" % constant_number
paul@251 813
                    structure.append("%s /* %s */" % (constant_value, attrname))
paul@251 814
                    continue
paul@251 815
paul@318 816
                # Special class relationship attributes.
paul@318 817
paul@318 818
                elif is_type_attribute(attrname):
paul@318 819
                    structure.append("{0, &%s}" % encode_path(decode_type_attribute(attrname)))
paul@318 820
                    continue
paul@318 821
paul@126 822
                structure.append(self.encode_member(origin, attrname, attr, kind))
paul@126 823
paul@126 824
    def encode_member(self, path, name, ref, structure_type):
paul@126 825
paul@126 826
        """
paul@126 827
        Encode within the structure provided by 'path', the member whose 'name'
paul@126 828
        provides 'ref', within the given 'structure_type'.
paul@126 829
        """
paul@126 830
paul@126 831
        kind = ref.get_kind()
paul@126 832
        origin = ref.get_origin()
paul@126 833
paul@126 834
        # References to constant literals.
paul@126 835
paul@126 836
        if kind == "<instance>":
paul@126 837
            attr_path = "%s.%s" % (path, name)
paul@126 838
paul@126 839
            # Obtain a constant value directly assigned to the attribute.
paul@126 840
paul@126 841
            if self.optimiser.constant_numbers.has_key(attr_path):
paul@126 842
                constant_number = self.optimiser.constant_numbers[attr_path]
paul@247 843
                constant_value = "__const%d" % constant_number
paul@247 844
                return "%s /* %s */" % (constant_value, name)
paul@126 845
paul@136 846
        # Predefined constant references.
paul@136 847
paul@136 848
        if (path, name) in self.predefined_constant_members:
paul@136 849
            attr_path = encode_predefined_reference("%s.%s" % (path, name))
paul@136 850
            return "{&%s, &%s} /* %s */" % (attr_path, attr_path, name)
paul@136 851
paul@126 852
        # General undetermined members.
paul@126 853
paul@126 854
        if kind in ("<var>", "<instance>"):
paul@126 855
            return "{0, 0} /* %s */" % name
paul@126 856
paul@126 857
        # Set the context depending on the kind of attribute.
paul@139 858
        # For methods:          {&<parent>, &<attr>}
paul@126 859
        # For other attributes: {&<attr>, &<attr>}
paul@126 860
paul@126 861
        else:
paul@139 862
            if kind == "<function>" and structure_type == "<class>":
paul@139 863
                parent = origin.rsplit(".", 1)[0]
paul@139 864
                context = "&%s" % encode_path(parent)
paul@139 865
            elif kind == "<instance>":
paul@139 866
                context = "&%s" % encode_path(origin)
paul@139 867
            else:
paul@139 868
                context = "0"
paul@126 869
            return "{%s, &%s}" % (context, encode_path(origin))
paul@126 870
paul@126 871
    def append_defaults(self, path, structure):
paul@126 872
paul@126 873
        """
paul@126 874
        For the given 'path', append default parameter members to the given
paul@126 875
        'structure'.
paul@126 876
        """
paul@126 877
paul@126 878
        for name, default in self.importer.function_defaults.get(path):
paul@126 879
            structure.append(self.encode_member(path, name, default, "<instance>"))
paul@126 880
paul@159 881
    def write_instantiator(self, f_code, f_signatures, path, init_ref):
paul@126 882
paul@126 883
        """
paul@159 884
        Write an instantiator to 'f_code', with a signature to 'f_signatures',
paul@159 885
        for instances of the class with the given 'path', with 'init_ref' as the
paul@159 886
        initialiser function reference.
paul@126 887
paul@126 888
        NOTE: This also needs to initialise any __fn__ and __args__ members
paul@126 889
        NOTE: where __call__ is provided by the class.
paul@126 890
        """
paul@126 891
paul@132 892
        parameters = self.importer.function_parameters[init_ref.get_origin()]
paul@126 893
paul@126 894
        print >>f_code, """\
paul@132 895
__attr %s(__attr __args[])
paul@126 896
{
paul@159 897
    /* Allocate the structure. */
paul@132 898
    __args[0] = __new(&%s, &%s, sizeof(%s));
paul@159 899
paul@159 900
    /* Call the initialiser. */
paul@146 901
    %s(__args);
paul@159 902
paul@159 903
    /* Return the allocated object details. */
paul@132 904
    return __args[0];
paul@126 905
}
paul@126 906
""" % (
paul@126 907
    encode_instantiator_pointer(path),
paul@150 908
    encode_tablename("<instance>", path),
paul@150 909
    encode_path(path),
paul@150 910
    encode_symbol("obj", path),
paul@126 911
    encode_function_pointer(init_ref.get_origin())
paul@126 912
    )
paul@126 913
paul@291 914
        print >>f_signatures, "#define __HAVE_%s" % encode_path(path)
paul@159 915
        print >>f_signatures, "__attr %s(__attr[]);" % encode_instantiator_pointer(path)
paul@159 916
paul@159 917
        # Write additional literal instantiators. These do not call the
paul@159 918
        # initialisers but instead populate the structures directly.
paul@159 919
paul@159 920
        if path in self.literal_instantiator_types:
paul@283 921
            if path in self.literal_mapping_types:
paul@283 922
                style = "mapping"
paul@283 923
            else:
paul@283 924
                style = "sequence"
paul@283 925
paul@159 926
            print >>f_code, """\
paul@159 927
__attr %s(__attr __args[], unsigned int number)
paul@159 928
{
paul@159 929
    /* Allocate the structure. */
paul@159 930
    __args[0] = __new(&%s, &%s, sizeof(%s));
paul@159 931
paul@283 932
    /* Allocate a structure for the data and set it on the __data__ attribute. */
paul@283 933
    %s(__args, number);
paul@159 934
paul@159 935
    /* Return the allocated object details. */
paul@159 936
    return __args[0];
paul@159 937
}
paul@159 938
""" % (
paul@159 939
    encode_literal_instantiator(path),
paul@159 940
    encode_tablename("<instance>", path),
paul@159 941
    encode_path(path),
paul@159 942
    encode_symbol("obj", path),
paul@283 943
    encode_literal_data_initialiser(style)
paul@159 944
    )
paul@159 945
paul@232 946
            print >>f_signatures, "__attr %s(__attr[], unsigned int);" % encode_literal_instantiator(path)
paul@159 947
paul@146 948
    def write_main_program(self, f_code, f_signatures):
paul@146 949
paul@146 950
        """
paul@146 951
        Write the main program to 'f_code', invoking the program's modules. Also
paul@146 952
        write declarations for module main functions to 'f_signatures'.
paul@146 953
        """
paul@146 954
paul@146 955
        print >>f_code, """\
paul@146 956
int main(int argc, char *argv[])
paul@159 957
{
paul@190 958
    __exc __tmp_exc;
paul@272 959
paul@159 960
    __Try
paul@159 961
    {"""
paul@146 962
paul@186 963
        for name in self.importer.order_modules():
paul@146 964
            function_name = "__main_%s" % encode_path(name)
paul@146 965
            print >>f_signatures, "void %s();" % function_name
paul@146 966
paul@161 967
            # Omit the native module.
paul@146 968
paul@186 969
            if name != "native":
paul@146 970
                print >>f_code, """\
paul@165 971
        %s();""" % function_name
paul@146 972
paul@146 973
        print >>f_code, """\
paul@159 974
        return 0;
paul@159 975
    }
paul@190 976
    __Catch(__tmp_exc)
paul@159 977
    {
paul@273 978
        fprintf(stderr, "Program terminated due to exception: %%s.\\n",
paul@272 979
                __load_via_object(
paul@273 980
                    %s((__attr[]) {{0, 0}, __tmp_exc.arg}).value,
paul@273 981
                    %s).strvalue);
paul@159 982
        return 1;
paul@159 983
    }
paul@146 984
}
paul@273 985
""" % (
paul@273 986
    encode_function_pointer("__builtins__.str.str"),
paul@273 987
    encode_symbol("pos", "__data__")
paul@273 988
    )
paul@146 989
paul@126 990
# vim: tabstop=4 expandtab shiftwidth=4