Lichen

Annotated translator.py

639:93254c89358b
2017-02-28 Paul Boddie Added testing of temporary result variable availability.
paul@113 1
#!/usr/bin/env python
paul@113 2
paul@113 3
"""
paul@113 4
Translate programs.
paul@113 5
paul@482 6
Copyright (C) 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>
paul@113 7
paul@113 8
This program is free software; you can redistribute it and/or modify it under
paul@113 9
the terms of the GNU General Public License as published by the Free Software
paul@113 10
Foundation; either version 3 of the License, or (at your option) any later
paul@113 11
version.
paul@113 12
paul@113 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@113 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@113 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@113 16
details.
paul@113 17
paul@113 18
You should have received a copy of the GNU General Public License along with
paul@113 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@113 20
"""
paul@113 21
paul@636 22
from common import CommonModule, CommonOutput, \
paul@609 23
                   first, get_builtin_class, init_item, is_newer, \
paul@609 24
                   predefined_constants
paul@618 25
from encoders import encode_access_instruction, encode_access_instruction_arg, \
paul@636 26
                     encode_function_pointer, encode_literal_instantiator, \
paul@636 27
                     encode_instantiator_pointer, encode_path, encode_symbol, \
paul@636 28
                     encode_type_attribute, is_type_attribute
paul@545 29
from errors import InspectError, TranslateError
paul@113 30
from os.path import exists, join
paul@113 31
from os import makedirs
paul@314 32
from referencing import Reference
paul@636 33
from results import Result
paul@636 34
from transresults import TrConstantValueRef, TrInstanceRef, \
paul@636 35
                         TrLiteralSequenceRef, TrResolvedNameRef, \
paul@636 36
                         AttrResult, Expression, InstantiationResult, \
paul@636 37
                         InvocationResult, LogicalOperationResult, \
paul@636 38
                         LogicalResult, NegationResult, PredefinedConstantRef, \
paul@636 39
                         ReturnRef
paul@482 40
from StringIO import StringIO
paul@113 41
import compiler
paul@556 42
import sys
paul@113 43
paul@113 44
class Translator(CommonOutput):
paul@113 45
paul@113 46
    "A program translator."
paul@113 47
paul@113 48
    def __init__(self, importer, deducer, optimiser, output):
paul@113 49
        self.importer = importer
paul@113 50
        self.deducer = deducer
paul@113 51
        self.optimiser = optimiser
paul@113 52
        self.output = output
paul@113 53
paul@633 54
    def to_output(self, reset=False, debug=False, gc_sections=False):
paul@609 55
paul@609 56
        "Write a program to the configured output directory."
paul@609 57
paul@609 58
        # Make a directory for the final sources.
paul@609 59
paul@113 60
        output = join(self.output, "src")
paul@113 61
paul@113 62
        if not exists(output):
paul@113 63
            makedirs(output)
paul@113 64
paul@609 65
        # Clean the output directory of irrelevant data.
paul@609 66
paul@617 67
        self.check_output("debug=%r gc_sections=%r" % (debug, gc_sections))
paul@113 68
paul@113 69
        for module in self.importer.modules.values():
paul@609 70
            output_filename = join(output, "%s.c" % module.name)
paul@609 71
paul@609 72
            # Do not generate modules in the native package. They are provided
paul@609 73
            # by native functionality source files.
paul@609 74
paul@354 75
            parts = module.name.split(".")
paul@609 76
paul@633 77
            if parts[0] != "native" and \
paul@633 78
               (reset or is_newer(module.filename, output_filename)):
paul@633 79
paul@161 80
                tm = TranslatedModule(module.name, self.importer, self.deducer, self.optimiser)
paul@609 81
                tm.translate(module.filename, output_filename)
paul@113 82
paul@636 83
paul@629 84
paul@113 85
def make_expression(expr):
paul@113 86
paul@113 87
    "Make a new expression from the existing 'expr'."
paul@113 88
paul@636 89
    if isinstance(expr, Result):
paul@113 90
        return expr
paul@113 91
    else:
paul@113 92
        return Expression(str(expr))
paul@113 93
paul@552 94
paul@552 95
paul@113 96
# The actual translation process itself.
paul@113 97
paul@113 98
class TranslatedModule(CommonModule):
paul@113 99
paul@113 100
    "A module translator."
paul@113 101
paul@113 102
    def __init__(self, name, importer, deducer, optimiser):
paul@113 103
        CommonModule.__init__(self, name, importer)
paul@113 104
        self.deducer = deducer
paul@113 105
        self.optimiser = optimiser
paul@113 106
paul@113 107
        # Output stream.
paul@113 108
paul@482 109
        self.out_toplevel = self.out = None
paul@113 110
        self.indent = 0
paul@113 111
        self.tabstop = "    "
paul@113 112
paul@113 113
        # Recorded namespaces.
paul@113 114
paul@113 115
        self.namespaces = []
paul@113 116
        self.in_conditional = False
paul@113 117
paul@144 118
        # Exception raising adjustments.
paul@144 119
paul@144 120
        self.in_try_finally = False
paul@189 121
        self.in_try_except = False
paul@144 122
paul@237 123
        # Attribute access and accessor counting.
paul@113 124
paul@113 125
        self.attr_accesses = {}
paul@237 126
        self.attr_accessors = {}
paul@113 127
paul@482 128
        # Special variable usage.
paul@482 129
paul@482 130
        self.temp_usage = {}
paul@482 131
paul@593 132
        # Initialise some data used for attribute access generation.
paul@593 133
paul@593 134
        self.init_substitutions()
paul@593 135
paul@113 136
    def __repr__(self):
paul@113 137
        return "TranslatedModule(%r, %r)" % (self.name, self.importer)
paul@113 138
paul@113 139
    def translate(self, filename, output_filename):
paul@113 140
paul@113 141
        """
paul@113 142
        Parse the file having the given 'filename', writing the translation to
paul@113 143
        the given 'output_filename'.
paul@113 144
        """
paul@113 145
paul@113 146
        self.parse_file(filename)
paul@113 147
paul@113 148
        # Collect function namespaces for separate processing.
paul@113 149
paul@113 150
        self.record_namespaces(self.astnode)
paul@113 151
paul@113 152
        # Reset the lambda naming (in order to obtain the same names again) and
paul@113 153
        # translate the program.
paul@113 154
paul@113 155
        self.reset_lambdas()
paul@113 156
paul@482 157
        self.out_toplevel = self.out = open(output_filename, "w")
paul@113 158
        try:
paul@128 159
            self.start_output()
paul@128 160
paul@113 161
            # Process namespaces, writing the translation.
paul@113 162
paul@113 163
            for path, node in self.namespaces:
paul@113 164
                self.process_namespace(path, node)
paul@113 165
paul@113 166
            # Process the module namespace including class namespaces.
paul@113 167
paul@113 168
            self.process_namespace([], self.astnode)
paul@113 169
paul@113 170
        finally:
paul@113 171
            self.out.close()
paul@113 172
paul@113 173
    def have_object(self):
paul@113 174
paul@113 175
        "Return whether a namespace is a recorded object."
paul@113 176
paul@113 177
        return self.importer.objects.get(self.get_namespace_path())
paul@113 178
paul@156 179
    def get_builtin_class(self, name):
paul@156 180
paul@156 181
        "Return a reference to the actual object providing 'name'."
paul@156 182
paul@538 183
        return self.importer.get_object(get_builtin_class(name))
paul@156 184
paul@156 185
    def is_method(self, path):
paul@156 186
paul@156 187
        "Return whether 'path' is a method."
paul@156 188
paul@113 189
        class_name, method_name = path.rsplit(".", 1)
paul@267 190
        return self.importer.classes.has_key(class_name) and class_name or None
paul@113 191
paul@208 192
    def in_method(self):
paul@208 193
paul@208 194
        "Return whether the current namespace provides a method."
paul@208 195
paul@208 196
        return self.in_function and self.is_method(self.get_namespace_path())
paul@208 197
paul@113 198
    # Namespace recording.
paul@113 199
paul@113 200
    def record_namespaces(self, node):
paul@113 201
paul@113 202
        "Process the program structure 'node', recording namespaces."
paul@113 203
paul@113 204
        for n in node.getChildNodes():
paul@113 205
            self.record_namespaces_in_node(n)
paul@113 206
paul@113 207
    def record_namespaces_in_node(self, node):
paul@113 208
paul@113 209
        "Process the program structure 'node', recording namespaces."
paul@113 210
paul@113 211
        # Function namespaces within modules, classes and other functions.
paul@113 212
        # Functions appearing within conditional statements are given arbitrary
paul@113 213
        # names.
paul@113 214
paul@113 215
        if isinstance(node, compiler.ast.Function):
paul@113 216
            self.record_function_node(node, (self.in_conditional or self.in_function) and self.get_lambda_name() or node.name)
paul@113 217
paul@113 218
        elif isinstance(node, compiler.ast.Lambda):
paul@113 219
            self.record_function_node(node, self.get_lambda_name())
paul@113 220
paul@113 221
        # Classes are visited, but may be ignored if inside functions.
paul@113 222
paul@113 223
        elif isinstance(node, compiler.ast.Class):
paul@113 224
            self.enter_namespace(node.name)
paul@113 225
            if self.have_object():
paul@113 226
                self.record_namespaces(node)
paul@113 227
            self.exit_namespace()
paul@113 228
paul@113 229
        # Conditional nodes are tracked so that function definitions may be
paul@113 230
        # handled. Since "for" loops are converted to "while" loops, they are
paul@113 231
        # included here.
paul@113 232
paul@113 233
        elif isinstance(node, (compiler.ast.For, compiler.ast.If, compiler.ast.While)):
paul@113 234
            in_conditional = self.in_conditional
paul@113 235
            self.in_conditional = True
paul@113 236
            self.record_namespaces(node)
paul@113 237
            self.in_conditional = in_conditional
paul@113 238
paul@113 239
        # All other nodes are processed depth-first.
paul@113 240
paul@113 241
        else:
paul@113 242
            self.record_namespaces(node)
paul@113 243
paul@113 244
    def record_function_node(self, n, name):
paul@113 245
paul@113 246
        """
paul@113 247
        Record the given function, lambda, if expression or list comprehension
paul@113 248
        node 'n' with the given 'name'.
paul@113 249
        """
paul@113 250
paul@113 251
        self.in_function = True
paul@113 252
        self.enter_namespace(name)
paul@113 253
paul@113 254
        if self.have_object():
paul@113 255
paul@113 256
            # Record the namespace path and the node itself.
paul@113 257
paul@113 258
            self.namespaces.append((self.namespace_path[:], n))
paul@113 259
            self.record_namespaces_in_node(n.code)
paul@113 260
paul@113 261
        self.exit_namespace()
paul@113 262
        self.in_function = False
paul@113 263
paul@113 264
    # Constant referencing.
paul@113 265
paul@405 266
    def get_literal_instance(self, n, name=None):
paul@113 267
paul@113 268
        """
paul@405 269
        For node 'n', return a reference for the type of the given 'name', or if
paul@405 270
        'name' is not specified, deduce the type from the value.
paul@113 271
        """
paul@113 272
paul@366 273
        # Handle stray None constants (Sliceobj seems to produce them).
paul@366 274
paul@405 275
        if name is None and n.value is None:
paul@366 276
            return self.process_name_node(compiler.ast.Name("None"))
paul@366 277
paul@113 278
        if name in ("dict", "list", "tuple"):
paul@405 279
            ref = self.get_builtin_class(name)
paul@113 280
            return self.process_literal_sequence_node(n, name, ref, TrLiteralSequenceRef)
paul@113 281
        else:
paul@537 282
            value, typename, encoding = self.get_constant_value(n.value, n.literals)
paul@538 283
            ref = self.get_builtin_class(typename)
paul@397 284
            value_type = ref.get_origin()
paul@397 285
paul@113 286
            path = self.get_namespace_path()
paul@406 287
paul@406 288
            # Obtain the local numbering of the constant and thus the
paul@406 289
            # locally-qualified name.
paul@406 290
paul@406 291
            local_number = self.importer.all_constants[path][(value, value_type, encoding)]
paul@113 292
            constant_name = "$c%d" % local_number
paul@113 293
            objpath = self.get_object_path(constant_name)
paul@406 294
paul@406 295
            # Obtain the unique identifier for the constant.
paul@406 296
paul@113 297
            number = self.optimiser.constant_numbers[objpath]
paul@394 298
            return TrConstantValueRef(constant_name, ref.instance_of(), value, number)
paul@113 299
paul@113 300
    # Namespace translation.
paul@113 301
paul@113 302
    def process_namespace(self, path, node):
paul@113 303
paul@113 304
        """
paul@113 305
        Process the namespace for the given 'path' defined by the given 'node'.
paul@113 306
        """
paul@113 307
paul@113 308
        self.namespace_path = path
paul@113 309
paul@113 310
        if isinstance(node, (compiler.ast.Function, compiler.ast.Lambda)):
paul@113 311
            self.in_function = True
paul@113 312
            self.process_function_body_node(node)
paul@113 313
        else:
paul@113 314
            self.in_function = False
paul@192 315
            self.function_target = 0
paul@113 316
            self.start_module()
paul@113 317
            self.process_structure(node)
paul@113 318
            self.end_module()
paul@113 319
paul@113 320
    def process_structure(self, node):
paul@113 321
paul@113 322
        "Process the given 'node' or result."
paul@113 323
paul@144 324
        # Handle processing requests on results.
paul@144 325
paul@636 326
        if isinstance(node, Result):
paul@113 327
            return node
paul@144 328
paul@144 329
        # Handle processing requests on nodes.
paul@144 330
paul@113 331
        else:
paul@144 332
            l = CommonModule.process_structure(self, node)
paul@144 333
paul@144 334
            # Return indications of return statement usage.
paul@144 335
paul@144 336
            if l and isinstance(l[-1], ReturnRef):
paul@144 337
                return l[-1]
paul@144 338
            else:
paul@144 339
                return None
paul@113 340
paul@113 341
    def process_structure_node(self, n):
paul@113 342
paul@113 343
        "Process the individual node 'n'."
paul@113 344
paul@113 345
        # Plain statements emit their expressions.
paul@113 346
paul@113 347
        if isinstance(n, compiler.ast.Discard):
paul@113 348
            expr = self.process_structure_node(n.expr)
paul@113 349
            self.statement(expr)
paul@113 350
paul@314 351
        # Module import declarations.
paul@314 352
paul@314 353
        elif isinstance(n, compiler.ast.From):
paul@314 354
            self.process_from_node(n)
paul@314 355
paul@113 356
        # Nodes using operator module functions.
paul@113 357
paul@113 358
        elif isinstance(n, compiler.ast.Operator):
paul@113 359
            return self.process_operator_node(n)
paul@113 360
paul@113 361
        elif isinstance(n, compiler.ast.AugAssign):
paul@113 362
            self.process_augassign_node(n)
paul@113 363
paul@113 364
        elif isinstance(n, compiler.ast.Compare):
paul@113 365
            return self.process_compare_node(n)
paul@113 366
paul@113 367
        elif isinstance(n, compiler.ast.Slice):
paul@113 368
            return self.process_slice_node(n)
paul@113 369
paul@113 370
        elif isinstance(n, compiler.ast.Sliceobj):
paul@113 371
            return self.process_sliceobj_node(n)
paul@113 372
paul@113 373
        elif isinstance(n, compiler.ast.Subscript):
paul@113 374
            return self.process_subscript_node(n)
paul@113 375
paul@113 376
        # Classes are visited, but may be ignored if inside functions.
paul@113 377
paul@113 378
        elif isinstance(n, compiler.ast.Class):
paul@113 379
            self.process_class_node(n)
paul@113 380
paul@113 381
        # Functions within namespaces have any dynamic defaults initialised.
paul@113 382
paul@113 383
        elif isinstance(n, compiler.ast.Function):
paul@113 384
            self.process_function_node(n)
paul@113 385
paul@113 386
        # Lambdas are replaced with references to separately-generated
paul@113 387
        # functions.
paul@113 388
paul@113 389
        elif isinstance(n, compiler.ast.Lambda):
paul@113 390
            return self.process_lambda_node(n)
paul@113 391
paul@113 392
        # Assignments.
paul@113 393
paul@113 394
        elif isinstance(n, compiler.ast.Assign):
paul@113 395
paul@113 396
            # Handle each assignment node.
paul@113 397
paul@113 398
            for node in n.nodes:
paul@113 399
                self.process_assignment_node(node, n.expr)
paul@113 400
paul@113 401
        # Accesses.
paul@113 402
paul@113 403
        elif isinstance(n, compiler.ast.Getattr):
paul@113 404
            return self.process_attribute_access(n)
paul@113 405
paul@113 406
        # Names.
paul@113 407
paul@113 408
        elif isinstance(n, compiler.ast.Name):
paul@113 409
            return self.process_name_node(n)
paul@113 410
paul@113 411
        # Loops and conditionals.
paul@113 412
paul@113 413
        elif isinstance(n, compiler.ast.For):
paul@113 414
            self.process_for_node(n)
paul@113 415
paul@113 416
        elif isinstance(n, compiler.ast.While):
paul@113 417
            self.process_while_node(n)
paul@113 418
paul@113 419
        elif isinstance(n, compiler.ast.If):
paul@113 420
            self.process_if_node(n)
paul@113 421
paul@113 422
        elif isinstance(n, (compiler.ast.And, compiler.ast.Or)):
paul@113 423
            return self.process_logical_node(n)
paul@113 424
paul@113 425
        elif isinstance(n, compiler.ast.Not):
paul@113 426
            return self.process_not_node(n)
paul@113 427
paul@113 428
        # Exception control-flow tracking.
paul@113 429
paul@113 430
        elif isinstance(n, compiler.ast.TryExcept):
paul@113 431
            self.process_try_node(n)
paul@113 432
paul@113 433
        elif isinstance(n, compiler.ast.TryFinally):
paul@113 434
            self.process_try_finally_node(n)
paul@113 435
paul@113 436
        # Control-flow modification statements.
paul@113 437
paul@113 438
        elif isinstance(n, compiler.ast.Break):
paul@128 439
            self.writestmt("break;")
paul@113 440
paul@113 441
        elif isinstance(n, compiler.ast.Continue):
paul@128 442
            self.writestmt("continue;")
paul@113 443
paul@144 444
        elif isinstance(n, compiler.ast.Raise):
paul@144 445
            self.process_raise_node(n)
paul@144 446
paul@113 447
        elif isinstance(n, compiler.ast.Return):
paul@144 448
            return self.process_return_node(n)
paul@113 449
paul@173 450
        # Print statements.
paul@173 451
paul@173 452
        elif isinstance(n, (compiler.ast.Print, compiler.ast.Printnl)):
paul@173 453
            self.statement(self.process_print_node(n))
paul@173 454
paul@113 455
        # Invocations.
paul@113 456
paul@113 457
        elif isinstance(n, compiler.ast.CallFunc):
paul@113 458
            return self.process_invocation_node(n)
paul@113 459
paul@113 460
        elif isinstance(n, compiler.ast.Keyword):
paul@113 461
            return self.process_structure_node(n.expr)
paul@113 462
paul@113 463
        # Constant usage.
paul@113 464
paul@113 465
        elif isinstance(n, compiler.ast.Const):
paul@405 466
            return self.get_literal_instance(n)
paul@113 467
paul@113 468
        elif isinstance(n, compiler.ast.Dict):
paul@113 469
            return self.get_literal_instance(n, "dict")
paul@113 470
paul@113 471
        elif isinstance(n, compiler.ast.List):
paul@113 472
            return self.get_literal_instance(n, "list")
paul@113 473
paul@113 474
        elif isinstance(n, compiler.ast.Tuple):
paul@113 475
            return self.get_literal_instance(n, "tuple")
paul@113 476
paul@113 477
        # All other nodes are processed depth-first.
paul@113 478
paul@113 479
        else:
paul@144 480
            return self.process_structure(n)
paul@113 481
paul@113 482
    def process_assignment_node(self, n, expr):
paul@113 483
paul@113 484
        "Process the individual node 'n' to be assigned the contents of 'expr'."
paul@113 485
paul@113 486
        # Names and attributes are assigned the entire expression.
paul@113 487
paul@113 488
        if isinstance(n, compiler.ast.AssName):
paul@113 489
            name_ref = self.process_name_node(n, self.process_structure_node(expr))
paul@113 490
            self.statement(name_ref)
paul@113 491
paul@238 492
            # Employ guards after assignments if required.
paul@238 493
paul@238 494
            if expr and name_ref.is_name():
paul@238 495
                self.generate_guard(name_ref.name)
paul@238 496
paul@113 497
        elif isinstance(n, compiler.ast.AssAttr):
paul@124 498
            in_assignment = self.in_assignment
paul@124 499
            self.in_assignment = self.process_structure_node(expr)
paul@124 500
            self.statement(self.process_attribute_access(n))
paul@124 501
            self.in_assignment = in_assignment
paul@113 502
paul@113 503
        # Lists and tuples are matched against the expression and their
paul@113 504
        # items assigned to expression items.
paul@113 505
paul@113 506
        elif isinstance(n, (compiler.ast.AssList, compiler.ast.AssTuple)):
paul@113 507
            self.process_assignment_node_items(n, expr)
paul@113 508
paul@113 509
        # Slices and subscripts are permitted within assignment nodes.
paul@113 510
paul@113 511
        elif isinstance(n, compiler.ast.Slice):
paul@113 512
            self.statement(self.process_slice_node(n, expr))
paul@113 513
paul@113 514
        elif isinstance(n, compiler.ast.Subscript):
paul@113 515
            self.statement(self.process_subscript_node(n, expr))
paul@113 516
paul@124 517
    def process_attribute_access(self, n):
paul@113 518
paul@368 519
        "Process the given attribute access node 'n'."
paul@113 520
paul@113 521
        # Obtain any completed chain and return the reference to it.
paul@113 522
paul@113 523
        attr_expr = self.process_attribute_chain(n)
paul@113 524
        if self.have_access_expression(n):
paul@113 525
            return attr_expr
paul@113 526
paul@113 527
        # Where the start of the chain of attributes has been reached, process
paul@113 528
        # the complete access.
paul@113 529
paul@113 530
        name_ref = attr_expr and attr_expr.is_name() and attr_expr
paul@603 531
        name = name_ref and self.get_name_for_tracking(name_ref.name, name_ref) or None
paul@113 532
paul@553 533
        location = self.get_access_location(name, self.attrs)
paul@113 534
        refs = self.get_referenced_attributes(location)
paul@113 535
paul@113 536
        # Generate access instructions.
paul@113 537
paul@113 538
        subs = {
paul@491 539
            "<expr>" : attr_expr,
paul@618 540
            "<name>" : "%s.value" % attr_expr,
paul@491 541
            "<assexpr>" : self.in_assignment,
paul@482 542
            }
paul@482 543
paul@593 544
        subs.update(self.temp_subs)
paul@593 545
        subs.update(self.op_subs)
paul@482 546
paul@113 547
        output = []
paul@482 548
        substituted = set()
paul@482 549
paul@591 550
        # The context set or retrieved will be that used by any enclosing
paul@591 551
        # invocation.
paul@591 552
paul@591 553
        context_index = self.function_target - 1
paul@618 554
        context_identity = None
paul@591 555
paul@482 556
        # Obtain encoded versions of each instruction, accumulating temporary
paul@482 557
        # variables.
paul@113 558
paul@113 559
        for instruction in self.optimiser.access_instructions[location]:
paul@618 560
paul@618 561
            # Intercept a special instruction identifying the context.
paul@618 562
paul@618 563
            if instruction[0] == "<context_identity>":
paul@618 564
                context_identity, _substituted = encode_access_instruction_arg(instruction[1], subs, instruction[0], context_index)
paul@618 565
                continue
paul@618 566
paul@618 567
            # Collect the encoded instruction, noting any temporary variables
paul@618 568
            # required by it.
paul@618 569
paul@591 570
            encoded, _substituted = encode_access_instruction(instruction, subs, context_index)
paul@482 571
            output.append(encoded)
paul@482 572
            substituted.update(_substituted)
paul@482 573
paul@482 574
        # Record temporary name usage.
paul@482 575
paul@482 576
        for sub in substituted:
paul@593 577
            if self.temp_subs.has_key(sub):
paul@593 578
                self.record_temp(self.temp_subs[sub])
paul@482 579
paul@113 580
        del self.attrs[0]
paul@618 581
        return AttrResult(output, refs, location, context_identity)
paul@113 582
paul@593 583
    def init_substitutions(self):
paul@593 584
paul@593 585
        """
paul@593 586
        Initialise substitutions, defining temporary variable mappings, some of
paul@593 587
        which are also used as substitutions, together with operation mappings
paul@593 588
        used as substitutions in instructions defined by the optimiser.
paul@593 589
        """
paul@593 590
paul@593 591
        self.temp_subs = {
paul@593 592
paul@593 593
            # Substitutions used by instructions.
paul@593 594
paul@593 595
            "<private_context>" : "__tmp_private_context",
paul@593 596
            "<accessor>" : "__tmp_value",
paul@593 597
            "<target_accessor>" : "__tmp_target_value",
paul@593 598
paul@593 599
            # Mappings to be replaced by those given below.
paul@593 600
paul@593 601
            "<context>" : "__tmp_contexts",
paul@601 602
            "<test_context_revert>" : "__tmp_contexts",
paul@595 603
            "<test_context_static>" : "__tmp_contexts",
paul@593 604
            "<set_context>" : "__tmp_contexts",
paul@593 605
            "<set_private_context>" : "__tmp_private_context",
paul@593 606
            "<set_accessor>" : "__tmp_value",
paul@593 607
            "<set_target_accessor>" : "__tmp_target_value",
paul@593 608
            }
paul@593 609
paul@593 610
        self.op_subs = {
paul@593 611
            "<context>" : "__get_context",
paul@601 612
            "<test_context_revert>" : "__test_context_revert",
paul@595 613
            "<test_context_static>" : "__test_context_static",
paul@593 614
            "<set_context>" : "__set_context",
paul@593 615
            "<set_private_context>" : "__set_private_context",
paul@593 616
            "<set_accessor>" : "__set_accessor",
paul@593 617
            "<set_target_accessor>" : "__set_target_accessor",
paul@593 618
            }
paul@593 619
paul@113 620
    def get_referenced_attributes(self, location):
paul@113 621
paul@113 622
        """
paul@113 623
        Convert 'location' to the form used by the deducer and retrieve any
paul@553 624
        identified attributes.
paul@113 625
        """
paul@113 626
paul@113 627
        access_location = self.deducer.const_accesses.get(location)
paul@113 628
        refs = []
paul@113 629
        for attrtype, objpath, attr in self.deducer.referenced_attrs[access_location or location]:
paul@113 630
            refs.append(attr)
paul@113 631
        return refs
paul@113 632
paul@553 633
    def get_referenced_attribute_invocations(self, location):
paul@553 634
paul@553 635
        """
paul@553 636
        Convert 'location' to the form used by the deducer and retrieve any
paul@553 637
        identified attribute invocation details.
paul@553 638
        """
paul@553 639
paul@553 640
        access_location = self.deducer.const_accesses.get(location)
paul@553 641
        return self.deducer.reference_invocations_unsuitable.get(access_location or location)
paul@553 642
paul@234 643
    def get_accessor_kinds(self, location):
paul@234 644
paul@234 645
        "Return the accessor kinds for 'location'."
paul@234 646
paul@554 647
        return self.optimiser.accessor_kinds.get(location)
paul@234 648
paul@553 649
    def get_access_location(self, name, attrnames=None):
paul@113 650
paul@113 651
        """
paul@553 652
        Using the current namespace, the given 'name', and the 'attrnames'
paul@553 653
        employed in an access, return the access location.
paul@113 654
        """
paul@113 655
paul@113 656
        path = self.get_path_for_access()
paul@113 657
paul@113 658
        # Get the location used by the deducer and optimiser and find any
paul@113 659
        # recorded access.
paul@113 660
paul@553 661
        attrnames = attrnames and ".".join(self.attrs)
paul@113 662
        access_number = self.get_access_number(path, name, attrnames)
paul@113 663
        self.update_access_number(path, name, attrnames)
paul@113 664
        return (path, name, attrnames, access_number)
paul@113 665
paul@113 666
    def get_access_number(self, path, name, attrnames):
paul@113 667
        access = name, attrnames
paul@113 668
        if self.attr_accesses.has_key(path) and self.attr_accesses[path].has_key(access):
paul@113 669
            return self.attr_accesses[path][access]
paul@113 670
        else:
paul@113 671
            return 0
paul@113 672
paul@113 673
    def update_access_number(self, path, name, attrnames):
paul@113 674
        access = name, attrnames
paul@113 675
        if name:
paul@113 676
            init_item(self.attr_accesses, path, dict)
paul@144 677
            init_item(self.attr_accesses[path], access, lambda: 0)
paul@144 678
            self.attr_accesses[path][access] += 1
paul@113 679
paul@237 680
    def get_accessor_location(self, name):
paul@237 681
paul@237 682
        """
paul@237 683
        Using the current namespace and the given 'name', return the accessor
paul@237 684
        location.
paul@237 685
        """
paul@237 686
paul@237 687
        path = self.get_path_for_access()
paul@237 688
paul@237 689
        # Get the location used by the deducer and optimiser and find any
paul@237 690
        # recorded accessor.
paul@237 691
paul@237 692
        access_number = self.get_accessor_number(path, name)
paul@237 693
        self.update_accessor_number(path, name)
paul@237 694
        return (path, name, None, access_number)
paul@237 695
paul@237 696
    def get_accessor_number(self, path, name):
paul@237 697
        if self.attr_accessors.has_key(path) and self.attr_accessors[path].has_key(name):
paul@237 698
            return self.attr_accessors[path][name]
paul@237 699
        else:
paul@237 700
            return 0
paul@237 701
paul@237 702
    def update_accessor_number(self, path, name):
paul@237 703
        if name:
paul@237 704
            init_item(self.attr_accessors, path, dict)
paul@237 705
            init_item(self.attr_accessors[path], name, lambda: 0)
paul@237 706
            self.attr_accessors[path][name] += 1
paul@237 707
paul@113 708
    def process_class_node(self, n):
paul@113 709
paul@113 710
        "Process the given class node 'n'."
paul@113 711
paul@320 712
        class_name = self.get_object_path(n.name)
paul@320 713
paul@320 714
        # Where a class is set conditionally or where the name may refer to
paul@320 715
        # different values, assign the name.
paul@320 716
paul@320 717
        ref = self.importer.identify(class_name)
paul@320 718
paul@320 719
        if not ref.static():
paul@626 720
            self.process_assignment_for_object(n.name,
paul@626 721
                make_expression("__ATTRVALUE(&%s)" % encode_path(class_name)))
paul@320 722
paul@113 723
        self.enter_namespace(n.name)
paul@113 724
paul@113 725
        if self.have_object():
paul@113 726
            self.write_comment("Class: %s" % class_name)
paul@113 727
paul@257 728
            self.initialise_inherited_members(class_name)
paul@257 729
paul@113 730
            self.process_structure(n)
paul@257 731
            self.write_comment("End class: %s" % class_name)
paul@113 732
paul@113 733
        self.exit_namespace()
paul@113 734
paul@257 735
    def initialise_inherited_members(self, class_name):
paul@257 736
paul@257 737
        "Initialise members of 'class_name' inherited from its ancestors."
paul@257 738
paul@257 739
        for name, path in self.importer.all_class_attrs[class_name].items():
paul@257 740
            target = "%s.%s" % (class_name, name)
paul@257 741
paul@257 742
            # Ignore attributes with definitions.
paul@257 743
paul@257 744
            ref = self.importer.identify(target)
paul@257 745
            if ref:
paul@257 746
                continue
paul@257 747
paul@320 748
            # Ignore special type attributes.
paul@320 749
paul@320 750
            if is_type_attribute(name):
paul@320 751
                continue
paul@320 752
paul@257 753
            # Reference inherited attributes.
paul@257 754
paul@257 755
            ref = self.importer.identify(path)
paul@257 756
            if ref and not ref.static():
paul@257 757
                parent, attrname = path.rsplit(".", 1)
paul@257 758
paul@257 759
                self.writestmt("__store_via_object(&%s, %s, __load_via_object(&%s, %s));" % (
paul@624 760
                    encode_path(class_name), name,
paul@624 761
                    encode_path(parent), attrname
paul@257 762
                    ))
paul@257 763
paul@314 764
    def process_from_node(self, n):
paul@314 765
paul@314 766
        "Process the given node 'n', importing from another module."
paul@314 767
paul@314 768
        path = self.get_namespace_path()
paul@314 769
paul@314 770
        # Attempt to obtain the referenced objects.
paul@314 771
paul@314 772
        for name, alias in n.names:
paul@314 773
            if name == "*":
paul@314 774
                raise InspectError("Only explicitly specified names can be imported from modules.", path, n)
paul@314 775
paul@314 776
            # Obtain the path of the assigned name.
paul@314 777
paul@314 778
            objpath = self.get_object_path(alias or name)
paul@314 779
paul@314 780
            # Obtain the identity of the name.
paul@314 781
paul@314 782
            ref = self.importer.identify(objpath)
paul@314 783
paul@314 784
            # Where the name is not static, assign the value.
paul@314 785
paul@314 786
            if ref and not ref.static() and ref.get_name():
paul@314 787
                self.writestmt("%s;" % 
paul@314 788
                    TrResolvedNameRef(alias or name, Reference("<var>", None, objpath),
paul@314 789
                                      expr=TrResolvedNameRef(name, ref)))
paul@314 790
paul@113 791
    def process_function_body_node(self, n):
paul@113 792
paul@113 793
        """
paul@113 794
        Process the given function, lambda, if expression or list comprehension
paul@113 795
        node 'n', generating the body.
paul@113 796
        """
paul@113 797
paul@113 798
        function_name = self.get_namespace_path()
paul@113 799
        self.start_function(function_name)
paul@113 800
paul@113 801
        # Process the function body.
paul@113 802
paul@113 803
        in_conditional = self.in_conditional
paul@113 804
        self.in_conditional = False
paul@192 805
        self.function_target = 0
paul@113 806
paul@237 807
        # Process any guards defined for the parameters.
paul@237 808
paul@237 809
        for name in self.importer.function_parameters.get(function_name):
paul@238 810
            self.generate_guard(name)
paul@237 811
paul@237 812
        # Produce the body and any additional return statement.
paul@237 813
paul@144 814
        expr = self.process_structure_node(n.code) or PredefinedConstantRef("None")
paul@144 815
        if not isinstance(expr, ReturnRef):
paul@128 816
            self.writestmt("return %s;" % expr)
paul@113 817
paul@113 818
        self.in_conditional = in_conditional
paul@113 819
paul@144 820
        self.end_function(function_name)
paul@113 821
paul@238 822
    def generate_guard(self, name):
paul@238 823
paul@238 824
        """
paul@238 825
        Get the accessor details for 'name', found in the current namespace, and
paul@238 826
        generate any guards defined for it.
paul@238 827
        """
paul@238 828
paul@238 829
        # Obtain the location, keeping track of assignment versions.
paul@238 830
paul@238 831
        location = self.get_accessor_location(name)
paul@238 832
        test = self.deducer.accessor_guard_tests.get(location)
paul@238 833
paul@238 834
        # Generate any guard from the deduced information.
paul@238 835
paul@238 836
        if test:
paul@238 837
            guard, guard_type = test
paul@238 838
paul@238 839
            if guard == "specific":
paul@238 840
                ref = first(self.deducer.accessor_all_types[location])
paul@238 841
                argstr = "&%s" % encode_path(ref.get_origin())
paul@238 842
            elif guard == "common":
paul@238 843
                ref = first(self.deducer.accessor_all_general_types[location])
paul@624 844
                argstr = encode_path(encode_type_attribute(ref.get_origin()))
paul@238 845
            else:
paul@238 846
                return
paul@238 847
paul@257 848
            # Produce an appropriate access to an attribute's value.
paul@257 849
paul@257 850
            parameters = self.importer.function_parameters.get(self.get_namespace_path())
paul@257 851
            if parameters and name in parameters:
paul@257 852
                name_to_value = "%s->value" % name
paul@257 853
            else:
paul@257 854
                name_to_value = "%s.value" % name
paul@257 855
paul@238 856
            # Write a test that raises a TypeError upon failure.
paul@238 857
paul@257 858
            self.writestmt("if (!__test_%s_%s(%s, %s)) __raise_type_error();" % (
paul@257 859
                guard, guard_type, name_to_value, argstr))
paul@238 860
paul@113 861
    def process_function_node(self, n):
paul@113 862
paul@113 863
        """
paul@113 864
        Process the given function, lambda, if expression or list comprehension
paul@113 865
        node 'n', generating any initialisation statements.
paul@113 866
        """
paul@113 867
paul@113 868
        # Where a function is declared conditionally, use a separate name for
paul@113 869
        # the definition, and assign the definition to the stated name.
paul@113 870
paul@196 871
        original_name = n.name
paul@196 872
paul@113 873
        if self.in_conditional or self.in_function:
paul@113 874
            name = self.get_lambda_name()
paul@113 875
        else:
paul@113 876
            name = n.name
paul@113 877
paul@196 878
        objpath = self.get_object_path(name)
paul@196 879
paul@113 880
        # Obtain details of the defaults.
paul@113 881
paul@285 882
        defaults = self.process_function_defaults(n, name, objpath)
paul@113 883
        if defaults:
paul@113 884
            for default in defaults:
paul@113 885
                self.writeline("%s;" % default)
paul@113 886
paul@196 887
        # Where a function is set conditionally or where the name may refer to
paul@196 888
        # different values, assign the name.
paul@196 889
paul@196 890
        ref = self.importer.identify(objpath)
paul@113 891
paul@196 892
        if self.in_conditional or self.in_function:
paul@320 893
            self.process_assignment_for_object(original_name, compiler.ast.Name(name))
paul@196 894
        elif not ref.static():
paul@267 895
            context = self.is_method(objpath)
paul@267 896
paul@320 897
            self.process_assignment_for_object(original_name,
paul@626 898
                make_expression("__ATTRVALUE(&%s)" % encode_path(objpath)))
paul@113 899
paul@285 900
    def process_function_defaults(self, n, name, objpath, instance_name=None):
paul@113 901
paul@113 902
        """
paul@113 903
        Process the given function or lambda node 'n', initialising defaults
paul@113 904
        that are dynamically set. The given 'name' indicates the name of the
paul@285 905
        function. The given 'objpath' indicates the origin of the function.
paul@285 906
        The given 'instance_name' indicates the name of any separate instance
paul@285 907
        of the function created to hold the defaults.
paul@113 908
paul@113 909
        Return a list of operations setting defaults on a function instance.
paul@113 910
        """
paul@113 911
paul@113 912
        function_name = self.get_object_path(name)
paul@113 913
        function_defaults = self.importer.function_defaults.get(function_name)
paul@113 914
        if not function_defaults:
paul@113 915
            return None
paul@113 916
paul@113 917
        # Determine whether any unidentified defaults are involved.
paul@113 918
paul@285 919
        for argname, default in function_defaults:
paul@285 920
            if not default.static():
paul@285 921
                break
paul@285 922
        else:
paul@113 923
            return None
paul@113 924
paul@285 925
        # Handle bound methods.
paul@285 926
paul@285 927
        if not instance_name:
paul@523 928
            instance_name = "&%s" % encode_path(objpath)
paul@285 929
paul@113 930
        # Where defaults are involved but cannot be identified, obtain a new
paul@113 931
        # instance of the lambda and populate the defaults.
paul@113 932
paul@113 933
        defaults = []
paul@113 934
paul@113 935
        # Join the original defaults with the inspected defaults.
paul@113 936
paul@113 937
        original_defaults = [(argname, default) for (argname, default) in compiler.ast.get_defaults(n) if default]
paul@113 938
paul@113 939
        for i, (original, inspected) in enumerate(map(None, original_defaults, function_defaults)):
paul@113 940
paul@113 941
            # Obtain any reference for the default.
paul@113 942
paul@113 943
            if original:
paul@113 944
                argname, default = original
paul@113 945
                name_ref = self.process_structure_node(default)
paul@113 946
            elif inspected:
paul@113 947
                argname, default = inspected
paul@113 948
                name_ref = TrResolvedNameRef(argname, default)
paul@113 949
            else:
paul@113 950
                continue
paul@113 951
paul@338 952
            # Generate default initialisers except when constants are employed.
paul@338 953
            # Constants should be used when populating the function structures.
paul@338 954
paul@338 955
            if name_ref and not isinstance(name_ref, TrConstantValueRef):
paul@285 956
                defaults.append("__SETDEFAULT(%s, %s, %s)" % (instance_name, i, name_ref))
paul@113 957
paul@113 958
        return defaults
paul@113 959
paul@113 960
    def process_if_node(self, n):
paul@113 961
paul@113 962
        """
paul@113 963
        Process the given "if" node 'n'.
paul@113 964
        """
paul@113 965
paul@113 966
        first = True
paul@113 967
        for test, body in n.tests:
paul@113 968
            test_ref = self.process_structure_node(test)
paul@113 969
            self.start_if(first, test_ref)
paul@113 970
paul@113 971
            in_conditional = self.in_conditional
paul@113 972
            self.in_conditional = True
paul@113 973
            self.process_structure_node(body)
paul@113 974
            self.in_conditional = in_conditional
paul@113 975
paul@113 976
            self.end_if()
paul@113 977
            first = False
paul@113 978
paul@113 979
        if n.else_:
paul@113 980
            self.start_else()
paul@113 981
            self.process_structure_node(n.else_)
paul@113 982
            self.end_else()
paul@113 983
paul@634 984
        print >>self.out
paul@634 985
paul@113 986
    def process_invocation_node(self, n):
paul@113 987
paul@113 988
        "Process the given invocation node 'n'."
paul@113 989
paul@590 990
        # Any invocations in the expression will store target details in a
paul@590 991
        # different location.
paul@590 992
paul@590 993
        self.function_target += 1
paul@590 994
paul@590 995
        # Process the expression.
paul@590 996
paul@113 997
        expr = self.process_structure_node(n.node)
paul@590 998
paul@590 999
        # Reference the current target again.
paul@590 1000
paul@590 1001
        self.function_target -= 1
paul@590 1002
paul@590 1003
        # Obtain details of the invocation expression.
paul@590 1004
paul@113 1005
        objpath = expr.get_origin()
paul@554 1006
        location = expr.access_location()
paul@552 1007
paul@552 1008
        # Identified target details.
paul@552 1009
paul@118 1010
        target = None
paul@407 1011
        target_structure = None
paul@552 1012
paul@552 1013
        # Specific function target information.
paul@552 1014
paul@242 1015
        function = None
paul@552 1016
paul@552 1017
        # Instantiation involvement.
paul@552 1018
paul@317 1019
        instantiation = False
paul@159 1020
        literal_instantiation = False
paul@552 1021
paul@552 1022
        # Invocation requirements.
paul@552 1023
paul@312 1024
        context_required = True
paul@587 1025
        have_access_context = isinstance(expr, AttrResult)
paul@618 1026
        context_identity = have_access_context and expr.context()
paul@552 1027
        parameters = None
paul@552 1028
paul@552 1029
        # Obtain details of the callable and of its parameters.
paul@113 1030
paul@159 1031
        # Literals may be instantiated specially.
paul@159 1032
paul@159 1033
        if expr.is_name() and expr.name.startswith("$L") and objpath:
paul@317 1034
            instantiation = literal_instantiation = objpath
paul@159 1035
            target = encode_literal_instantiator(objpath)
paul@312 1036
            context_required = False
paul@159 1037
paul@159 1038
        # Identified targets employ function pointers directly.
paul@159 1039
paul@159 1040
        elif objpath:
paul@113 1041
            parameters = self.importer.function_parameters.get(objpath)
paul@234 1042
paul@234 1043
            # Class invocation involves instantiators.
paul@234 1044
paul@118 1045
            if expr.has_kind("<class>"):
paul@317 1046
                instantiation = objpath
paul@118 1047
                target = encode_instantiator_pointer(objpath)
paul@540 1048
                init_ref = self.importer.all_class_attrs[objpath]["__init__"]
paul@540 1049
                target_structure = "&%s" % encode_path(init_ref)
paul@312 1050
                context_required = False
paul@234 1051
paul@234 1052
            # Only plain functions and bound methods employ function pointers.
paul@234 1053
paul@118 1054
            elif expr.has_kind("<function>"):
paul@242 1055
                function = objpath
paul@234 1056
paul@234 1057
                # Test for functions and methods.
paul@234 1058
paul@407 1059
                context_required = self.is_method(objpath)
paul@554 1060
                accessor_kinds = self.get_accessor_kinds(location)
paul@312 1061
                instance_accessor = accessor_kinds and \
paul@312 1062
                                    len(accessor_kinds) == 1 and \
paul@312 1063
                                    first(accessor_kinds) == "<instance>"
paul@234 1064
paul@407 1065
                # Only identify certain bound methods or functions.
paul@407 1066
paul@407 1067
                if not context_required or instance_accessor:
paul@234 1068
                    target = encode_function_pointer(objpath)
paul@407 1069
paul@407 1070
                # Access bound method defaults even if it is not clear whether
paul@407 1071
                # the accessor is appropriate.
paul@407 1072
paul@523 1073
                target_structure = "&%s" % encode_path(objpath)
paul@312 1074
paul@558 1075
        # Other targets are retrieved at run-time. Some information about them
paul@558 1076
        # may be available and be used to provide warnings about argument
paul@558 1077
        # compatibility.
paul@558 1078
paul@558 1079
        elif self.importer.give_warning("args"):
paul@554 1080
            unsuitable = self.get_referenced_attribute_invocations(location)
paul@553 1081
paul@553 1082
            if unsuitable:
paul@553 1083
                for ref in unsuitable:
paul@552 1084
                    _objpath = ref.get_origin()
paul@553 1085
                    num_parameters = len(self.importer.function_parameters[_objpath])
paul@556 1086
                    print >>sys.stderr, \
paul@556 1087
                        "In %s, at line %d, inappropriate number of " \
paul@553 1088
                        "arguments given. Need %d arguments to call %s." % (
paul@553 1089
                        self.get_namespace_path(), n.lineno, num_parameters,
paul@553 1090
                        _objpath)
paul@113 1091
paul@619 1092
        # Determine any readily-accessible target identity.
paul@619 1093
paul@619 1094
        target_identity = target or expr.is_name() and str(expr) or None
paul@619 1095
        target_var = target_identity or "__tmp_targets[%d]" % self.function_target
paul@619 1096
paul@619 1097
        if not target_identity:
paul@619 1098
            self.record_temp("__tmp_targets")
paul@619 1099
paul@619 1100
        if context_identity and context_identity.startswith("__tmp_contexts"):
paul@619 1101
            self.record_temp("__tmp_contexts")
paul@619 1102
paul@122 1103
        # Arguments are presented in a temporary frame array with any context
paul@312 1104
        # always being the first argument. Where it would be unused, it may be
paul@312 1105
        # set to null.
paul@122 1106
paul@312 1107
        if context_required:
paul@587 1108
            if have_access_context:
paul@626 1109
                args = ["__ATTRVALUE(%s)" % context_identity]
paul@587 1110
            else:
paul@619 1111
                args = ["__CONTEXT_AS_VALUE(%s)" % target_var]
paul@312 1112
        else:
paul@477 1113
            args = ["__NULL"]
paul@312 1114
paul@552 1115
        # Complete the array with null values, permitting tests for a complete
paul@552 1116
        # set of arguments.
paul@552 1117
paul@122 1118
        args += [None] * (not parameters and len(n.args) or parameters and len(parameters) or 0)
paul@122 1119
        kwcodes = []
paul@122 1120
        kwargs = []
paul@122 1121
paul@192 1122
        # Any invocations in the arguments will store target details in a
paul@192 1123
        # different location.
paul@192 1124
paul@192 1125
        self.function_target += 1
paul@192 1126
paul@122 1127
        for i, arg in enumerate(n.args):
paul@122 1128
            argexpr = self.process_structure_node(arg)
paul@122 1129
paul@122 1130
            # Store a keyword argument, either in the argument list or
paul@122 1131
            # in a separate keyword argument list for subsequent lookup.
paul@122 1132
paul@122 1133
            if isinstance(arg, compiler.ast.Keyword):
paul@113 1134
paul@122 1135
                # With knowledge of the target, store the keyword
paul@122 1136
                # argument directly.
paul@122 1137
paul@122 1138
                if parameters:
paul@373 1139
                    try:
paul@373 1140
                        argnum = parameters.index(arg.name)
paul@373 1141
                    except ValueError:
paul@373 1142
                        raise TranslateError("Argument %s is not recognised." % arg.name,
paul@373 1143
                                             self.get_namespace_path(), n)
paul@122 1144
                    args[argnum+1] = str(argexpr)
paul@122 1145
paul@122 1146
                # Otherwise, store the details in a separate collection.
paul@122 1147
paul@122 1148
                else:
paul@122 1149
                    kwargs.append(str(argexpr))
paul@122 1150
                    kwcodes.append("{%s, %s}" % (
paul@623 1151
                        encode_ppos(arg.name), encode_pcode(arg.name)))
paul@122 1152
paul@312 1153
            # Store non-keyword arguments in the argument list, rejecting
paul@312 1154
            # superfluous arguments.
paul@312 1155
paul@122 1156
            else:
paul@225 1157
                try:
paul@225 1158
                    args[i+1] = str(argexpr)
paul@225 1159
                except IndexError:
paul@225 1160
                    raise TranslateError("Too many arguments specified.",
paul@225 1161
                                         self.get_namespace_path(), n)
paul@113 1162
paul@192 1163
        # Reference the current target again.
paul@192 1164
paul@192 1165
        self.function_target -= 1
paul@192 1166
paul@113 1167
        # Defaults are added to the frame where arguments are missing.
paul@113 1168
paul@122 1169
        if parameters:
paul@122 1170
            function_defaults = self.importer.function_defaults.get(objpath)
paul@122 1171
            if function_defaults:
paul@122 1172
paul@122 1173
                # Visit each default and set any missing arguments.
paul@149 1174
                # Use the target structure to obtain defaults, as opposed to the
paul@149 1175
                # actual function involved.
paul@122 1176
paul@122 1177
                for i, (argname, default) in enumerate(function_defaults):
paul@122 1178
                    argnum = parameters.index(argname)
paul@122 1179
                    if not args[argnum+1]:
paul@285 1180
                        args[argnum+1] = "__GETDEFAULT(%s, %d)" % (target_structure, i)
paul@149 1181
paul@173 1182
        # Test for missing arguments.
paul@173 1183
paul@173 1184
        if None in args:
paul@173 1185
            raise TranslateError("Not all arguments supplied.",
paul@173 1186
                                 self.get_namespace_path(), n)
paul@173 1187
paul@149 1188
        # Encode the arguments.
paul@122 1189
paul@122 1190
        argstr = "__ARGS(%s)" % ", ".join(args)
paul@122 1191
        kwargstr = kwargs and ("__ARGS(%s)" % ", ".join(kwargs)) or "0"
paul@122 1192
        kwcodestr = kwcodes and ("__KWARGS(%s)" % ", ".join(kwcodes)) or "0"
paul@122 1193
paul@159 1194
        # Where literal instantiation is occurring, add an argument indicating
paul@159 1195
        # the number of values.
paul@159 1196
paul@159 1197
        if literal_instantiation:
paul@159 1198
            argstr += ", %d" % (len(args) - 1)
paul@159 1199
paul@156 1200
        # First, the invocation expression is presented.
paul@113 1201
paul@156 1202
        stages = []
paul@156 1203
paul@156 1204
        # Without a known specific callable, the expression provides the target.
paul@118 1205
paul@312 1206
        if not target or context_required:
paul@588 1207
            if target:
paul@618 1208
                if expr:
paul@618 1209
                    stages.append(str(expr))
paul@619 1210
            elif not target_identity:
paul@619 1211
                stages.append("%s = %s" % (target_var, expr))
paul@156 1212
paul@156 1213
        # Any specific callable is then obtained.
paul@156 1214
paul@163 1215
        if target:
paul@156 1216
            stages.append(target)
paul@484 1217
paul@484 1218
        # Methods accessed via unidentified accessors are obtained. 
paul@484 1219
paul@242 1220
        elif function:
paul@523 1221
            if context_required:
paul@587 1222
                if have_access_context:
paul@619 1223
                    stages.append("__get_function(%s, %s)" % (
paul@619 1224
                        context_identity, target_var))
paul@587 1225
                else:
paul@619 1226
                    stages.append("__get_function(__CONTEXT_AS_VALUE(%s).value, %s)" % (
paul@619 1227
                        target_var, target_var))
paul@523 1228
            else:
paul@624 1229
                stages.append("__load_via_object(%s.value, __fn__).fn" % target_var)
paul@122 1230
paul@122 1231
        # With a known target, the function is obtained directly and called.
paul@484 1232
        # By putting the invocation at the end of the final element in the
paul@484 1233
        # instruction sequence (the stages), the result becomes the result of
paul@484 1234
        # the sequence. Moreover, the parameters become part of the sequence
paul@484 1235
        # and thereby participate in a guaranteed evaluation order.
paul@122 1236
paul@242 1237
        if target or function:
paul@498 1238
            stages[-1] += "(%s)" % argstr
paul@498 1239
            if instantiation:
paul@498 1240
                return InstantiationResult(instantiation, stages)
paul@498 1241
            else:
paul@498 1242
                return InvocationResult(stages)
paul@113 1243
paul@122 1244
        # With unknown targets, the generic invocation function is applied to
paul@122 1245
        # the callable and argument collections.
paul@113 1246
paul@122 1247
        else:
paul@619 1248
            stages.append("__invoke(\n%s,\n%d, %d, %s, %s,\n%d, %s\n)" % (
paul@619 1249
                target_var,
paul@156 1250
                self.always_callable and 1 or 0,
paul@122 1251
                len(kwargs), kwcodestr, kwargstr,
paul@498 1252
                len(args), argstr))
paul@498 1253
            return InvocationResult(stages)
paul@113 1254
paul@113 1255
    def always_callable(self, refs):
paul@113 1256
paul@113 1257
        "Determine whether all 'refs' are callable."
paul@113 1258
paul@113 1259
        for ref in refs:
paul@113 1260
            if not ref.static():
paul@113 1261
                return False
paul@113 1262
            else:
paul@113 1263
                origin = ref.final()
paul@113 1264
                if not self.importer.get_attribute(origin, "__fn__"):
paul@113 1265
                    return False
paul@113 1266
        return True
paul@113 1267
paul@113 1268
    def need_default_arguments(self, objpath, nargs):
paul@113 1269
paul@113 1270
        """
paul@113 1271
        Return whether any default arguments are needed when invoking the object
paul@113 1272
        given by 'objpath'.
paul@113 1273
        """
paul@113 1274
paul@113 1275
        parameters = self.importer.function_parameters.get(objpath)
paul@113 1276
        return nargs < len(parameters)
paul@113 1277
paul@113 1278
    def process_lambda_node(self, n):
paul@113 1279
paul@113 1280
        "Process the given lambda node 'n'."
paul@113 1281
paul@113 1282
        name = self.get_lambda_name()
paul@113 1283
        function_name = self.get_object_path(name)
paul@113 1284
paul@285 1285
        defaults = self.process_function_defaults(n, name, function_name, "__tmp_value")
paul@149 1286
paul@149 1287
        # Without defaults, produce an attribute referring to the function.
paul@149 1288
paul@113 1289
        if not defaults:
paul@626 1290
            return make_expression("__ATTRVALUE(&%s)" % encode_path(function_name))
paul@149 1291
paul@149 1292
        # With defaults, copy the function structure and set the defaults on the
paul@149 1293
        # copy.
paul@149 1294
paul@113 1295
        else:
paul@482 1296
            self.record_temp("__tmp_value")
paul@626 1297
            return make_expression("(__tmp_value = __COPY(&%s, sizeof(%s)), %s, __ATTRVALUE(__tmp_value))" % (
paul@151 1298
                encode_path(function_name),
paul@151 1299
                encode_symbol("obj", function_name),
paul@151 1300
                ", ".join(defaults)))
paul@113 1301
paul@113 1302
    def process_logical_node(self, n):
paul@113 1303
paul@631 1304
        "Process the given operator node 'n'."
paul@113 1305
paul@482 1306
        self.record_temp("__tmp_result")
paul@482 1307
paul@631 1308
        conjunction = isinstance(n, compiler.ast.And)
paul@141 1309
        results = []
paul@113 1310
paul@631 1311
        for node in n.nodes:
paul@631 1312
            results.append(self.process_structure_node(node))
paul@631 1313
paul@631 1314
        return LogicalOperationResult(results, conjunction)
paul@113 1315
paul@113 1316
    def process_name_node(self, n, expr=None):
paul@113 1317
paul@113 1318
        "Process the given name node 'n' with the optional assignment 'expr'."
paul@113 1319
paul@113 1320
        # Determine whether the name refers to a static external entity.
paul@113 1321
paul@113 1322
        if n.name in predefined_constants:
paul@399 1323
            return PredefinedConstantRef(n.name, expr)
paul@113 1324
paul@173 1325
        # Convert literal references, operator function names, and print
paul@173 1326
        # function names to references.
paul@113 1327
paul@173 1328
        elif n.name.startswith("$L") or n.name.startswith("$op") or \
paul@173 1329
             n.name.startswith("$print"):
paul@423 1330
paul@423 1331
            ref, paths = self.importer.get_module(self.name).special[n.name]
paul@113 1332
            return TrResolvedNameRef(n.name, ref)
paul@113 1333
paul@561 1334
        # Temporary names are output program locals.
paul@561 1335
paul@561 1336
        elif n.name.startswith("$t"):
paul@561 1337
            return TrResolvedNameRef(n.name, Reference("<var>"), expr=expr)
paul@561 1338
paul@113 1339
        # Get the appropriate name for the name reference, using the same method
paul@113 1340
        # as in the inspector.
paul@113 1341
paul@250 1342
        path = self.get_namespace_path()
paul@250 1343
        objpath = self.get_object_path(n.name)
paul@250 1344
paul@250 1345
        # Determine any assigned globals.
paul@250 1346
paul@250 1347
        globals = self.importer.get_module(self.name).scope_globals.get(path)
paul@603 1348
paul@603 1349
        # Explicitly declared globals.
paul@603 1350
paul@250 1351
        if globals and n.name in globals:
paul@250 1352
            objpath = self.get_global_path(n.name)
paul@603 1353
            is_global = True
paul@603 1354
paul@603 1355
        # Implicitly referenced globals in functions.
paul@603 1356
paul@603 1357
        elif self.in_function:
paul@603 1358
            is_global = n.name not in self.importer.function_locals[path]
paul@603 1359
paul@603 1360
        # Implicitly referenced globals elsewhere.
paul@603 1361
paul@603 1362
        else:
paul@603 1363
            namespace = self.importer.identify(path)
paul@603 1364
            is_global = not self.importer.get_attributes(namespace, n.name)
paul@113 1365
paul@113 1366
        # Get the static identity of the name.
paul@113 1367
paul@250 1368
        ref = self.importer.identify(objpath)
paul@152 1369
        if ref and not ref.get_name():
paul@250 1370
            ref = ref.alias(objpath)
paul@113 1371
paul@113 1372
        # Obtain any resolved names for non-assignment names.
paul@113 1373
paul@113 1374
        if not expr and not ref and self.in_function:
paul@250 1375
            locals = self.importer.function_locals.get(path)
paul@113 1376
            ref = locals and locals.get(n.name)
paul@113 1377
paul@208 1378
        # Determine whether the name refers to a parameter. The generation of
paul@208 1379
        # parameter references is different from other names.
paul@208 1380
paul@250 1381
        parameters = self.importer.function_parameters.get(path)
paul@208 1382
        parameter = n.name == "self" and self.in_method() or \
paul@208 1383
                    parameters and n.name in parameters
paul@208 1384
paul@553 1385
        # Find any invocation details.
paul@553 1386
paul@553 1387
        location = self.get_access_location(n.name)
paul@553 1388
paul@113 1389
        # Qualified names are used for resolved static references or for
paul@113 1390
        # static namespace members. The reference should be configured to return
paul@113 1391
        # such names.
paul@113 1392
paul@603 1393
        return TrResolvedNameRef(n.name, ref, expr=expr, is_global=is_global, parameter=parameter, location=location)
paul@113 1394
paul@113 1395
    def process_not_node(self, n):
paul@113 1396
paul@113 1397
        "Process the given operator node 'n'."
paul@113 1398
paul@638 1399
        return self.make_negation(self.process_structure_node(n.expr))
paul@144 1400
paul@144 1401
    def process_raise_node(self, n):
paul@144 1402
paul@144 1403
        "Process the given raise node 'n'."
paul@144 1404
paul@144 1405
        # NOTE: Determine which raise statement variants should be permitted.
paul@144 1406
paul@176 1407
        if n.expr1:
paul@467 1408
paul@467 1409
            # Names with accompanying arguments are treated like invocations.
paul@467 1410
paul@467 1411
            if n.expr2:
paul@467 1412
                call = compiler.ast.CallFunc(n.expr1, [n.expr2])
paul@467 1413
                exc = self.process_structure_node(call)
paul@467 1414
                self.writestmt("__Raise(%s);" % exc)
paul@317 1415
paul@317 1416
            # Raise instances, testing the kind at run-time if necessary and
paul@317 1417
            # instantiating any non-instance.
paul@317 1418
paul@317 1419
            else:
paul@467 1420
                exc = self.process_structure_node(n.expr1)
paul@467 1421
paul@467 1422
                if isinstance(exc, TrInstanceRef):
paul@467 1423
                    self.writestmt("__Raise(%s);" % exc)
paul@467 1424
                else:
paul@467 1425
                    self.writestmt("__Raise(__ensure_instance(%s));" % exc)
paul@176 1426
        else:
paul@346 1427
            self.writestmt("__Throw(__tmp_exc);")
paul@144 1428
paul@144 1429
    def process_return_node(self, n):
paul@144 1430
paul@144 1431
        "Process the given return node 'n'."
paul@144 1432
paul@144 1433
        expr = self.process_structure_node(n.value) or PredefinedConstantRef("None")
paul@189 1434
        if self.in_try_finally or self.in_try_except:
paul@144 1435
            self.writestmt("__Return(%s);" % expr)
paul@144 1436
        else:
paul@144 1437
            self.writestmt("return %s;" % expr)
paul@144 1438
paul@144 1439
        return ReturnRef()
paul@113 1440
paul@113 1441
    def process_try_node(self, n):
paul@113 1442
paul@113 1443
        """
paul@113 1444
        Process the given "try...except" node 'n'.
paul@113 1445
        """
paul@113 1446
paul@189 1447
        in_try_except = self.in_try_except
paul@189 1448
        self.in_try_except = True
paul@189 1449
paul@144 1450
        # Use macros to implement exception handling.
paul@113 1451
paul@144 1452
        self.writestmt("__Try")
paul@113 1453
        self.writeline("{")
paul@113 1454
        self.indent += 1
paul@113 1455
        self.process_structure_node(n.body)
paul@144 1456
paul@144 1457
        # Put the else statement in another try block that handles any raised
paul@144 1458
        # exceptions and converts them to exceptions that will not be handled by
paul@144 1459
        # the main handling block.
paul@144 1460
paul@144 1461
        if n.else_:
paul@144 1462
            self.writestmt("__Try")
paul@144 1463
            self.writeline("{")
paul@144 1464
            self.indent += 1
paul@144 1465
            self.process_structure_node(n.else_)
paul@144 1466
            self.indent -= 1
paul@144 1467
            self.writeline("}")
paul@144 1468
            self.writeline("__Catch (__tmp_exc)")
paul@144 1469
            self.writeline("{")
paul@144 1470
            self.indent += 1
paul@144 1471
            self.writeline("if (__tmp_exc.raising) __RaiseElse(__tmp_exc.arg);")
paul@191 1472
            self.writeline("else if (__tmp_exc.completing) __Throw(__tmp_exc);")
paul@144 1473
            self.indent -= 1
paul@144 1474
            self.writeline("}")
paul@144 1475
paul@144 1476
        # Complete the try block and enter the finally block, if appropriate.
paul@144 1477
paul@144 1478
        if self.in_try_finally:
paul@144 1479
            self.writestmt("__Complete;")
paul@144 1480
paul@113 1481
        self.indent -= 1
paul@113 1482
        self.writeline("}")
paul@113 1483
paul@189 1484
        self.in_try_except = in_try_except
paul@189 1485
paul@144 1486
        # Handlers are tests within a common handler block.
paul@144 1487
paul@144 1488
        self.writeline("__Catch (__tmp_exc)")
paul@144 1489
        self.writeline("{")
paul@144 1490
        self.indent += 1
paul@144 1491
paul@189 1492
        # Introduce an if statement to handle the completion of a try block.
paul@189 1493
paul@189 1494
        self.process_try_completion()
paul@189 1495
paul@144 1496
        # Handle exceptions in else blocks converted to __RaiseElse, converting
paul@144 1497
        # them back to normal exceptions.
paul@144 1498
paul@144 1499
        if n.else_:
paul@189 1500
            self.writeline("else if (__tmp_exc.raising_else) __Raise(__tmp_exc.arg);")
paul@144 1501
paul@144 1502
        # Exception handling.
paul@144 1503
paul@113 1504
        for name, var, handler in n.handlers:
paul@144 1505
paul@144 1506
            # Test for specific exceptions.
paul@144 1507
paul@113 1508
            if name is not None:
paul@113 1509
                name_ref = self.process_structure_node(name)
paul@462 1510
                self.writeline("else if (__ISINSTANCE(__tmp_exc.arg, %s))" % name_ref)
paul@144 1511
            else:
paul@189 1512
                self.writeline("else if (1)")
paul@113 1513
paul@113 1514
            self.writeline("{")
paul@113 1515
            self.indent += 1
paul@113 1516
paul@113 1517
            # Establish the local for the handler.
paul@113 1518
paul@113 1519
            if var is not None:
paul@261 1520
                self.writestmt("%s;" % self.process_name_node(var, make_expression("__tmp_exc.arg")))
paul@113 1521
paul@113 1522
            if handler is not None:
paul@113 1523
                self.process_structure_node(handler)
paul@113 1524
paul@113 1525
            self.indent -= 1
paul@113 1526
            self.writeline("}")
paul@113 1527
paul@144 1528
        # Re-raise unhandled exceptions.
paul@144 1529
paul@189 1530
        self.writeline("else __Throw(__tmp_exc);")
paul@144 1531
paul@144 1532
        # End the handler block.
paul@144 1533
paul@144 1534
        self.indent -= 1
paul@144 1535
        self.writeline("}")
paul@634 1536
        print >>self.out
paul@113 1537
paul@113 1538
    def process_try_finally_node(self, n):
paul@113 1539
paul@113 1540
        """
paul@113 1541
        Process the given "try...finally" node 'n'.
paul@113 1542
        """
paul@113 1543
paul@144 1544
        in_try_finally = self.in_try_finally
paul@144 1545
        self.in_try_finally = True
paul@113 1546
paul@144 1547
        # Use macros to implement exception handling.
paul@144 1548
paul@144 1549
        self.writestmt("__Try")
paul@113 1550
        self.writeline("{")
paul@113 1551
        self.indent += 1
paul@113 1552
        self.process_structure_node(n.body)
paul@113 1553
        self.indent -= 1
paul@113 1554
        self.writeline("}")
paul@144 1555
paul@144 1556
        self.in_try_finally = in_try_finally
paul@144 1557
paul@144 1558
        # Finally clauses handle special exceptions.
paul@144 1559
paul@144 1560
        self.writeline("__Catch (__tmp_exc)")
paul@113 1561
        self.writeline("{")
paul@113 1562
        self.indent += 1
paul@113 1563
        self.process_structure_node(n.final)
paul@144 1564
paul@189 1565
        # Introduce an if statement to handle the completion of a try block.
paul@189 1566
paul@189 1567
        self.process_try_completion()
paul@189 1568
        self.writeline("else __Throw(__tmp_exc);")
paul@189 1569
paul@189 1570
        self.indent -= 1
paul@189 1571
        self.writeline("}")
paul@634 1572
        print >>self.out
paul@189 1573
paul@189 1574
    def process_try_completion(self):
paul@189 1575
paul@189 1576
        "Generate a test for the completion of a try block."
paul@144 1577
paul@144 1578
        self.writestmt("if (__tmp_exc.completing)")
paul@144 1579
        self.writeline("{")
paul@144 1580
        self.indent += 1
paul@189 1581
paul@316 1582
        # Do not return anything at the module level.
paul@316 1583
paul@316 1584
        if self.get_namespace_path() != self.name:
paul@189 1585
paul@316 1586
            # Only use the normal return statement if no surrounding try blocks
paul@316 1587
            # apply.
paul@316 1588
paul@316 1589
            if not self.in_try_finally and not self.in_try_except:
paul@316 1590
                self.writeline("if (!__ISNULL(__tmp_exc.arg)) return __tmp_exc.arg;")
paul@316 1591
            else:
paul@316 1592
                self.writeline("if (!__ISNULL(__tmp_exc.arg)) __Throw(__tmp_exc);")
paul@144 1593
paul@113 1594
        self.indent -= 1
paul@113 1595
        self.writeline("}")
paul@113 1596
paul@113 1597
    def process_while_node(self, n):
paul@113 1598
paul@113 1599
        "Process the given while node 'n'."
paul@113 1600
paul@113 1601
        self.writeline("while (1)")
paul@113 1602
        self.writeline("{")
paul@113 1603
        self.indent += 1
paul@113 1604
        test = self.process_structure_node(n.test)
paul@113 1605
paul@113 1606
        # Emit the loop termination condition unless "while <true value>" is
paul@113 1607
        # indicated.
paul@113 1608
paul@113 1609
        if not (isinstance(test, PredefinedConstantRef) and test.value):
paul@113 1610
paul@629 1611
            # Emit a negated test of the continuation condition.
paul@629 1612
paul@638 1613
            self.start_if(True, self.make_negation(test))
paul@113 1614
            if n.else_:
paul@113 1615
                self.process_structure_node(n.else_)
paul@128 1616
            self.writestmt("break;")
paul@629 1617
            self.end_if()
paul@113 1618
paul@113 1619
        in_conditional = self.in_conditional
paul@113 1620
        self.in_conditional = True
paul@113 1621
        self.process_structure_node(n.body)
paul@113 1622
        self.in_conditional = in_conditional
paul@113 1623
paul@113 1624
        self.indent -= 1
paul@113 1625
        self.writeline("}")
paul@634 1626
        print >>self.out
paul@113 1627
paul@482 1628
    # Special variable usage.
paul@482 1629
paul@637 1630
    def get_temp_path(self):
paul@637 1631
paul@637 1632
        """
paul@637 1633
        Return the appropriate namespace path for temporary names in the current
paul@637 1634
        namespace.
paul@637 1635
        """
paul@637 1636
paul@637 1637
        if self.in_function:
paul@637 1638
            return self.get_namespace_path()
paul@637 1639
        else:
paul@637 1640
            return self.name
paul@637 1641
paul@482 1642
    def record_temp(self, name):
paul@482 1643
paul@482 1644
        """
paul@482 1645
        Record the use of the temporary 'name' in the current namespace. At the
paul@482 1646
        class or module level, the temporary name is associated with the module,
paul@482 1647
        since the variable will then be allocated in the module's own main
paul@482 1648
        program.
paul@482 1649
        """
paul@482 1650
paul@637 1651
        path = self.get_temp_path()
paul@637 1652
paul@637 1653
        init_item(self.temp_usage, path, list)
paul@637 1654
        self.temp_usage[path].append(name)
paul@637 1655
paul@637 1656
    def remove_temps(self, names):
paul@637 1657
paul@637 1658
        """
paul@637 1659
        Remove 'names' from temporary storage allocations, each instance
paul@637 1660
        removing each request for storage.
paul@637 1661
        """
paul@637 1662
paul@637 1663
        path = self.get_temp_path()
paul@637 1664
paul@637 1665
        for name in names:
paul@637 1666
            if self.uses_temp(path, name):
paul@637 1667
                self.temp_usage[path].remove(name)
paul@482 1668
paul@482 1669
    def uses_temp(self, path, name):
paul@482 1670
paul@482 1671
        """
paul@482 1672
        Return whether the given namespace 'path' employs a temporary variable
paul@482 1673
        with the given 'name'. Note that 'path' should only be a module or a
paul@482 1674
        function or method, not a class.
paul@482 1675
        """
paul@482 1676
paul@482 1677
        return self.temp_usage.has_key(path) and name in self.temp_usage[path]
paul@482 1678
paul@638 1679
    def make_negation(self, expr):
paul@638 1680
paul@638 1681
        "Return a negated form of 'expr'."
paul@638 1682
paul@638 1683
        result = NegationResult(expr)
paul@638 1684
paul@638 1685
        # Negation discards the temporary results of its operand.
paul@638 1686
paul@638 1687
        temps = expr.discards_temporary()
paul@638 1688
        if temps:
paul@638 1689
            self.remove_temps(temps)
paul@638 1690
paul@638 1691
        return result
paul@638 1692
paul@113 1693
    # Output generation.
paul@113 1694
paul@128 1695
    def start_output(self):
paul@159 1696
paul@159 1697
        "Write the declarations at the top of each source file."
paul@159 1698
paul@128 1699
        print >>self.out, """\
paul@128 1700
#include "types.h"
paul@144 1701
#include "exceptions.h"
paul@128 1702
#include "ops.h"
paul@128 1703
#include "progconsts.h"
paul@128 1704
#include "progops.h"
paul@128 1705
#include "progtypes.h"
paul@137 1706
#include "main.h"
paul@128 1707
"""
paul@128 1708
paul@482 1709
    def start_unit(self):
paul@482 1710
paul@482 1711
        "Record output within a generated function for later use."
paul@482 1712
paul@482 1713
        self.out = StringIO()
paul@482 1714
paul@482 1715
    def end_unit(self, name):
paul@482 1716
paul@482 1717
        "Add declarations and generated code."
paul@482 1718
paul@482 1719
        # Restore the output stream.
paul@482 1720
paul@482 1721
        out = self.out
paul@482 1722
        self.out = self.out_toplevel
paul@482 1723
paul@482 1724
        self.write_temporaries(name)
paul@634 1725
        print >>self.out
paul@482 1726
        out.seek(0)
paul@482 1727
        self.out.write(out.read())
paul@482 1728
paul@482 1729
        self.indent -= 1
paul@482 1730
        print >>self.out, "}"
paul@482 1731
paul@113 1732
    def start_module(self):
paul@159 1733
paul@159 1734
        "Write the start of each module's main function."
paul@159 1735
paul@113 1736
        print >>self.out, "void __main_%s()" % encode_path(self.name)
paul@113 1737
        print >>self.out, "{"
paul@113 1738
        self.indent += 1
paul@561 1739
paul@561 1740
        # Define temporary variables, excluded from the module structure itself.
paul@561 1741
paul@561 1742
        tempnames = []
paul@561 1743
paul@561 1744
        for n in self.importer.all_module_attrs[self.name]:
paul@561 1745
            if n.startswith("$t"):
paul@561 1746
                tempnames.append(encode_path(n))
paul@561 1747
paul@561 1748
        if tempnames:
paul@561 1749
            tempnames.sort()
paul@561 1750
            self.writeline("__attr %s;" % ", ".join(tempnames))
paul@561 1751
paul@482 1752
        self.start_unit()
paul@113 1753
paul@113 1754
    def end_module(self):
paul@159 1755
paul@159 1756
        "End each module by closing its main function."
paul@159 1757
paul@482 1758
        self.end_unit(self.name)
paul@113 1759
paul@113 1760
    def start_function(self, name):
paul@159 1761
paul@159 1762
        "Start the function having the given 'name'."
paul@159 1763
paul@113 1764
        print >>self.out, "__attr %s(__attr __args[])" % encode_function_pointer(name)
paul@113 1765
        print >>self.out, "{"
paul@113 1766
        self.indent += 1
paul@113 1767
paul@113 1768
        # Obtain local names from parameters.
paul@113 1769
paul@113 1770
        parameters = self.importer.function_parameters[name]
paul@144 1771
        locals = self.importer.function_locals[name].keys()
paul@113 1772
        names = []
paul@113 1773
paul@113 1774
        for n in locals:
paul@113 1775
paul@113 1776
            # Filter out special names and parameters. Note that self is a local
paul@113 1777
            # regardless of whether it originally appeared in the parameters or
paul@113 1778
            # not.
paul@113 1779
paul@113 1780
            if n.startswith("$l") or n in parameters or n == "self":
paul@113 1781
                continue
paul@113 1782
            names.append(encode_path(n))
paul@113 1783
paul@113 1784
        # Emit required local names.
paul@113 1785
paul@113 1786
        if names:
paul@113 1787
            names.sort()
paul@113 1788
            self.writeline("__attr %s;" % ", ".join(names))
paul@113 1789
paul@208 1790
        self.write_parameters(name)
paul@482 1791
        self.start_unit()
paul@144 1792
paul@144 1793
    def end_function(self, name):
paul@159 1794
paul@159 1795
        "End the function having the given 'name'."
paul@159 1796
paul@482 1797
        self.end_unit(name)
paul@144 1798
        print >>self.out
paul@144 1799
paul@482 1800
    def write_temporaries(self, name):
paul@482 1801
paul@482 1802
        "Write temporary storage employed by 'name'."
paul@482 1803
paul@482 1804
        # Provide space for the given number of targets.
paul@482 1805
paul@591 1806
        targets = self.importer.function_targets.get(name)
paul@591 1807
paul@482 1808
        if self.uses_temp(name, "__tmp_targets"):
paul@482 1809
            self.writeline("__attr __tmp_targets[%d];" % targets)
paul@591 1810
        if self.uses_temp(name, "__tmp_contexts"):
paul@591 1811
            self.writeline("__ref __tmp_contexts[%d];" % targets)
paul@482 1812
paul@482 1813
        # Add temporary variable usage details.
paul@482 1814
paul@592 1815
        if self.uses_temp(name, "__tmp_private_context"):
paul@592 1816
            self.writeline("__ref __tmp_private_context;")
paul@482 1817
        if self.uses_temp(name, "__tmp_value"):
paul@482 1818
            self.writeline("__ref __tmp_value;")
paul@482 1819
        if self.uses_temp(name, "__tmp_target_value"):
paul@482 1820
            self.writeline("__ref __tmp_target_value;")
paul@482 1821
        if self.uses_temp(name, "__tmp_result"):
paul@482 1822
            self.writeline("__attr __tmp_result;")
paul@479 1823
paul@479 1824
        module = self.importer.get_module(self.name)
paul@482 1825
paul@482 1826
        if name in module.exception_namespaces:
paul@479 1827
            self.writeline("__exc __tmp_exc;")
paul@149 1828
paul@208 1829
    def write_parameters(self, name):
paul@159 1830
paul@159 1831
        """
paul@159 1832
        For the function having the given 'name', write definitions of
paul@208 1833
        parameters found in the arguments array.
paul@159 1834
        """
paul@159 1835
paul@144 1836
        parameters = self.importer.function_parameters[name]
paul@144 1837
paul@113 1838
        # Generate any self reference.
paul@113 1839
paul@156 1840
        if self.is_method(name):
paul@208 1841
            self.writeline("__attr * const self = &__args[0];")
paul@113 1842
paul@113 1843
        # Generate aliases for the parameters.
paul@113 1844
paul@113 1845
        for i, parameter in enumerate(parameters):
paul@208 1846
            self.writeline("__attr * const %s = &__args[%d];" % (encode_path(parameter), i+1))
paul@113 1847
paul@631 1848
    def start_if(self, first, test_ref):
paul@629 1849
        statement = "%sif" % (not first and "else " or "")
paul@629 1850
paul@629 1851
        # Consume logical results directly.
paul@629 1852
paul@629 1853
        if isinstance(test_ref, LogicalResult):
paul@631 1854
            self.writeline("%s %s" % (statement, test_ref.apply_test()))
paul@637 1855
            temps = test_ref.discards_temporary()
paul@637 1856
            if temps:
paul@637 1857
                self.remove_temps(temps)
paul@629 1858
        else:
paul@631 1859
            self.writeline("%s (__BOOL(%s))" % (statement, test_ref))
paul@629 1860
paul@113 1861
        self.writeline("{")
paul@113 1862
        self.indent += 1
paul@113 1863
paul@113 1864
    def end_if(self):
paul@113 1865
        self.indent -= 1
paul@113 1866
        self.writeline("}")
paul@113 1867
paul@113 1868
    def start_else(self):
paul@113 1869
        self.writeline("else")
paul@113 1870
        self.writeline("{")
paul@113 1871
        self.indent += 1
paul@113 1872
paul@113 1873
    def end_else(self):
paul@113 1874
        self.indent -= 1
paul@113 1875
        self.writeline("}")
paul@113 1876
paul@113 1877
    def statement(self, expr):
paul@113 1878
        s = str(expr)
paul@113 1879
        if s:
paul@128 1880
            self.writestmt("%s;" % s)
paul@113 1881
paul@113 1882
    def statements(self, results):
paul@113 1883
        for result in results:
paul@113 1884
            self.statement(result)
paul@113 1885
paul@159 1886
    def writeline(self, s):
paul@159 1887
        print >>self.out, "%s%s" % (self.pad(), self.indenttext(s, self.indent + 1))
paul@159 1888
paul@159 1889
    def writestmt(self, s):
paul@159 1890
        self.writeline(s)
paul@159 1891
paul@159 1892
    def write_comment(self, s):
paul@159 1893
        self.writestmt("/* %s */" % s)
paul@159 1894
paul@113 1895
    def pad(self, extra=0):
paul@113 1896
        return (self.indent + extra) * self.tabstop
paul@113 1897
paul@113 1898
    def indenttext(self, s, levels):
paul@116 1899
        lines = s.split("\n")
paul@116 1900
        out = [lines[0]]
paul@116 1901
        for line in lines[1:]:
paul@116 1902
            out.append(levels * self.tabstop + line)
paul@116 1903
            if line.endswith("("):
paul@116 1904
                levels += 1
paul@122 1905
            elif line.startswith(")"):
paul@116 1906
                levels -= 1
paul@116 1907
        return "\n".join(out)
paul@113 1908
paul@113 1909
# vim: tabstop=4 expandtab shiftwidth=4