Lichen

Annotated generator.py

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