Lichen

Annotated generator.py

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